# 第九章:迭代器和生成器
# 9.1 python中的迭代协议
什么是迭代协议?
- Iterable
- Iterator
迭代器是什么?
迭代器是访问集合内元素的一种方式,一般用来遍历数据。 迭代器和以下标访问方式不一样,迭代器是不能返回的,迭代器提供了一种惰性访问数据的方式
from collections.abc import Iterable, Iterator
a = [1, 2]
print(isinstance(a, Iterable))
print(isinstance(a, Iterator))
# 9.2 什么是迭代器和可迭代对象
实现 iter 时,必须返回 Iterator 对象
from collections.abc import Iterator
class MyIterator(Iterator):
def __init__(self, employee):
self.employee = employee
self.index = 0
def __next__(self):
# 真正返回迭代值的逻辑
# 迭代器不支持切片,不会接收索引值,只能一步一步走
# 遍历大文件
try:
word = self.employee[self.index]
except IndexError:
raise StopIteration
self.index += 1
return word
class Company:
def __init__(self, employee):
self.employee = employee
# def __iter__(self):
# return 1 # TypeError: iter() returned non-iterator of type 'int'
# def __iter__(self):
# return self # TypeError: iter() returned non-iterator of type 'Company'
# 使用内置方法 iter
# def __iter__(self):
# return iter(self.employee) # <iterator object at 0x000001F512B907C8>
# 使用自定义 MyIterator ******
def __iter__(self):
return MyIterator(self.employee) # <__main__.MyIterator object at 0x0000013462EF0848>
def __getitem__(self, index):
return self.employee[index]
if __name__ == '__main__':
company = Company(['linda', 'alex', 'catherine'])
my_iterator = iter(company)
print(my_iterator)
# for 循环首先查找 __iter__;如果没有自动生成一个__iter__,里面遍历__getitem__
# for item in company:
# print(item)
while True:
try:
print(next(my_iterator))
except StopIteration:
break
"""
迭代器设计模式,不要在Company中实现 __next__ 方法,而要单独实现MyIterator实现,Company中__iter__调用MyIterator就行
"""
- Comanpy实例化的对象为可迭代对象,可用for循环遍历数据,内部实现了__iter__方法,该方法返回迭代器
- MyIterator实现化对象为迭代器,可用next()获取数值,内部实现了__iter__和__next__方法
# 9.3 生成器函数的使用
生成器函数,函数里包含 yield 关键字
- yield
- 不再是普通的函数
def gen_func():
yield 1
yield 2
yield 3
# 惰性求值,延迟求值提供了可能性
# 斐波拉契函数 0 1 1 2 3 5 8 ...
def fib(index):
if index <= 2:
return 1
else:
return fib(index-1) + fib(index-2)
def func():
return 1
if __name__ == '__main__':
# 返回为生成器对象,python编译字节码的时候产生
gen = gen_func()
# 生成器对象也是实现了迭代协议的,可以for循环
for value in gen:
print(value)
ret = func()
- 执行生成器函数得到生成器对象,可for循环取值
- 生成器函数可以多次返回值,流程的变化
# 获取对应位置的值
def fib(index):
if index <= 2:
return 1
else:
return fib(index-1) + fib(index-2)
# 获取整个过程
def fib2(index):
ret_list = []
n, a, b = 0, 0, 1
while n < index:
ret_list.append(b)
a, b = b, a + b
n += 1
return ret_list
# yield
def gen_fib(index):
n, a, b = 0, 0, 1
while n < index:
yield b
a, b = b, a + b
n += 1
print(fib(10))
print(fib2(10))
for value in gen_fib(10):
print(value)
斐波拉契 1 1 2 3 5 8 ...
- 根据位置获取对应值
- 根据位置获取所有值
# 9.4 python是如何实现生成器的
- 什么场景下运用生成器
- 生成器内部实现原理
- 生成器函数与普通函数区别
# python中函数工作原理
python.exe会用一个叫做PyEval_EvalFrameEx(c函数)去执行foo函数 首先会创建一个栈帧(stack_frame),一个上下文
import inspect
frame = None
def foo():
bar()
def bar():
global frame
frame = inspect.currentframe()
# 查看函数实现原理
# import dis
# print(dis.dis(foo))
foo()
print(frame.f_code.co_name) # 函数名 bar
caller_frame = frame.f_back
print(caller_frame.f_code.co_name) # 函数名 foo
python中一切皆对象,栈帧对象中运行foo函数字节码对象 当foo调用子函数bar,又会创建一个栈帧对象,在此栈帧对象中运行bar函数字节码对象
所有的栈帧都是分配再堆内存上(不会自动释放),这就对定了栈帧可以独立于调用者存在;不用于静态语言的调用,静态语言是栈的形式,调用完就自动释放
# python中生成器函数工作原理
def gen_func():
address = 'China'
yield 1
name = 'linda'
yield 2
age = 20
return 'done'
gen = gen_func()
import dis
print(dis.dis(gen))
print(gen.gi_frame.f_lasti)
print(gen.gi_frame.f_locals)
print('\nfirst value: %s' % next(gen))
print(gen.gi_frame.f_lasti)
print(gen.gi_frame.f_locals)
print('\nsecond value: %s' % next(gen))
print(gen.gi_frame.f_lasti)
print(gen.gi_frame.f_locals)
- 控制整个生成器函数暂定和继续前进 gen.gi_frame.f_lasti
- 整个生成器函数作用域逐渐变化 gen.gi_frame.f_locals
# 9.5 生成器在UserList中的应用
- from collections import UserList
class MyList:
def __init__(self):
self.data = []
def __getitem__(self, index):
return self.data[index]
def __setitem__(self, index, value):
self.data[index] = value
def insert(self, index, item):
self.data.insert(index, item)
ll = MyList()
ll.insert(0, 1)
ll.insert(0, 2)
ll.insert(0, 3)
print(ll.data)
- from collections import UserDict
class MyDict:
def __init__(self):
self.data = {}
def __getitem__(self, item):
return self.data[item]
def __setitem__(self, key, value):
self.data[key] = value
def update(self, **kw):
for key, value in kw.items():
self[key] = value
dd = MyDict()
print(dd.data)
dd.update(key1='value1', key2='value2')
print(dd['key1'])
print(dd.data)
具体源码分析参考模块 collections
# 9.6 生成器如何读取大文件
场景:500G 文件 特殊只有一行,特殊分割符号 {|}
def my_readline(f, newline):
buf = ''
while True:
while newline in buf:
pos = buf.index(newline)
yield buf[:pos]
buf = buf[pos + len(newline):]
chunk = f.read(4096 * 10)
if not chunk:
yield buf
break
buf += chunk
with open('input') as f:
for line in my_readline(f, '{|}'):
print(line)
← 第八章:元类编程