第九章 迭代器和生成器
9.1 Python的迭代协议
- 类中实现了__iter__方法,生成的对象,就是可迭代对象,可以使用for循环取值。
- 类中同时实现了__iter__和__next__语法,则生成的对象就是迭代器,可以使用next方法取值。
- 生成器是迭代器的子类,所以说生成器一定是迭代器,同时生成器中还必须实现send和throw两个抽象方法,从abc模块中,我们可以清晰地看到,Iterable,Iterator和Generator的关系。所以自己去读源码往往是最正确而且有效的方式,网上阅读大量的资料,博客最后还是分不清这三者的关系。与此同时,Python中所有collocation的子类生成的对象都是可迭代对象。迭代器提供了一种惰性方式数据的方式。
from collections.abc import Iterable, Iterator
a = list([1, 2])
print (isinstance(a, Iterable))
# True
9.2 生成器函数的使用
函数中一旦有了yield关键字,那么调用函数就会返回一个生成器。
def gen_fib(index):
n,a,b = 0,0,1
while n<index:
yield b
a,b = b, a+b
n += 1
for data in gen_fib(10):
print (data)
# 1
# 1
# 2
# 3
# 5
# 8
# 13
# 21
# 34
# 55
9.3 生成器的原理
- Python函数中调用函数会生成栈帧,由于栈帧保存在堆内存上,所以,栈帧可以独立于调用者存在。而生成器原理类似,在yield的同时,栈帧和next会保存起来,下一次调用时会继续执行,类似于恢复现场。
下面展示一些内联代码片
。
def gen_fib(index):
n,a,b = 0,0,1
while n<index:
yield b
a,b = b, a+b
n += 1
import dis
gen = gen_fib(3)
print (dis.dis(gen))
for i in gen:
print(gen.gi_frame.f_lasti)
print(gen.gi_frame.f_locals)
# 29 0 LOAD_CONST 1 ((0, 0, 1))
# 2 UNPACK_SEQUENCE 3
# 4 STORE_FAST 1 (n)
# 6 STORE_FAST 2 (a)
# 8 STORE_FAST 3 (b)
#
# 30 >> 10 LOAD_FAST 1 (n)
# 12 LOAD_FAST 0 (index)
# 14 COMPARE_OP 0 (<)
# 16 POP_JUMP_IF_FALSE 48
#
# 31 18 LOAD_FAST 3 (b)
# 20 YIELD_VALUE
# 22 POP_TOP
#
# 32 24 LOAD_FAST 3 (b)
# 26 LOAD_FAST 2 (a)
# 28 LOAD_FAST 3 (b)
# 30 BINARY_ADD
# 32 ROT_TWO
# 34 STORE_FAST 2 (a)
# 36 STORE_FAST 3 (b)
#
# 33 38 LOAD_FAST 1 (n)
# 40 LOAD_CONST 2 (1)
# 42 INPLACE_ADD
# 44 STORE_FAST 1 (n)
# 46 JUMP_ABSOLUTE 10
# >> 48 LOAD_CONST 0 (None)
# 50 RETURN_VALUE