Python生成器处理序列时允许序列中的每一个值只在需要时才计算,而不是提前计算。
举个例子,当我们要遍历2的32次方的整数时,如果用一个列表来存这么多数字,内存直接就被撑爆了。相反我们如果使用生成器,则可以在需要时再计算列表中的值。
>> 2**32
4294967296
for x in range(2**32)] >> L = [x
我们将上面的例子转换为一个生成器,看下生成器是如何工作的:
def bigList(n):
i = 0
while i < n:
yield i
i = i + 1
L = bigList(2*32)
for i in L:
print(i)
此时得到的 L 并不是一个列表,它实际上是一个生成器。语句运行到 yield 时暂停并将结果返回给调用者。与 return 语句不同的是, yield 语句不会终止函数执行,而是暂时在返回的位置等待下次调用生成器。
生成器也是函数,它使用 yield 语句代替 return。Python 提供了一个内置函数 next 用来请求生成器的下一个值。
2*32) > L = bigList(
> next(L)
0
> next(L)
1
> next(L)
2
> next(L)
3
其中 next 是单向的,如果我们想回退怎么办?这里有一个内置方法 send 可以干预 yield 值,相当于给 yield 重新传了一个值。这里需要将生成器改造一下,方便 send 传值。
def bigList(n):
i = 0
while i < n:
ret = yield i
if ret != None:
i = int(ret)
else:
i = i + 1
当调用 send 时,值被传回了 yield i 表达式。此时返回值不为 None,通过 if 语句更改生成器逻辑重新生成 i 的值,然后进入下一轮循环再次卡在 yield 语句。
当正常调用 next 时,yield 语句返回 None。当调用 send 时,语句从 yield 语句开始执行,传回来的值进入 yield 返回一个非 None 值,然后接着执行下面的语句。执行完后进入下一轮循环,然后再次调用 yield 语句返回。也就是说 send 语句通过传值更改执行逻辑后再次调用 yield 语句返回了一个值。在 yield 语句中打上输出标记可以看到执行的逻辑。
def bigList(n):
i = 0
while i < n:
ret = yield '!',i
print('# ', ret)
print()
if ret != None:
i = int(ret)
else:
i = i + 1
print('## ', i)
L = bigList(2**32)
print(next(L))
print('#'*10)
print('### ', L.send(111))
print('#'*10)
print('### ', L.send(0))
全文完。
评论