以下内容参考自《Python学习手册》。
在Python中,有两种语言结构可以延迟产生结果:
(一)生成器函数
1. 状态挂起
和返回一个值并退出的常规函数不同,生成器函数自动在生成值的时刻挂起并继续函数的执行。从函数的角度来看,这允许代码随着时间产生一系列的值,而不是一次性计算它们并在诸如列表的内容中返回它们。
2. 迭代协议整合
生成器函数返回的是一个可迭代的对象,该对象具有next方法(Python3重命名为__next__)来继续进行下一步的迭代,也可以通过使用内置的next方法来调用生成器对象的next方法。从调用者的角度来看,生成器的next方法继续函数并且运行到下一个yield结果返回或引发一个StopIteration异常。
e.g.
>>> def gen(N):
... for i in range(N):
... yield i ** 2
...
>>> gen(2)
>>> for i in gen(2):
... print i,
...
0 1
>>> G = gen(2)
>>> G.next()
0
>>> next(G)
1
3. 生成器的send方法
生成器的send方法主要用于每一步迭代中外部和生成器的信息传递,可以在生成器挂起时唤醒下一步的迭代。主要的使用方法是将yield表达式作为右值,那么左值就是send方法传递过来的值。如果仅以yield表达式作为右值,那么可以不加括号,e.g. X= yield Y;否则必须加上括号,e.g. X= (yield Y) ** 2。
e.g.
>>> def gen(N):
... for i in range(N):
... X = yield i
... print 'send:', X
...
>>> G = gen(10)
>>> next(G)
0
>>> G.send(10)
send: 10
1
>>> next(G)
send: None
2
第一句next(G)访问到 yield i 返回0,然后挂起;第二句G.send(10)唤醒生成器,并且把10传递进去,所以会打印 send: 10,之后访问到下一次循环的yield i返回1,然后挂起;第三局next(G)唤醒生成器,但是由于没有传递数据进去,所以X去默认值None,打印出 send: None,之后访问到下一次循环的yield i返回2,然后挂起。
(二)生成器表达式
>>> (x ** 2 for x in range(4))
at 0x0000000002FBD3F0>
>>> list(x ** 2 for x in range(4))
[0, 1, 4, 9]
>>> G = (x ** 2 for x in range(4))
>>> next(G)
0
>>> G.next()
1
PS:python中所有的解析方式(后两种是python2.7以后才有)
>>> [x for x in range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> (x for x in range(10))
at 0x00000000034903A8>
>>> {x for x in range(10)}
set([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> {x:x for x in range(10)}
{0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9}
(三)生成器是单次迭代对象
生成器函数和生成器表达式自身都是迭代器,并且只支持一次活跃迭代。如果你手动地使用多个迭代器来迭代结果流,它们将会指向相同的位置。
>>> G = (x ** 2 for x in range(4))
>>> iter(G) is G
True
>>> I1 = iter(G)
>>> next(I1)
0
>>> I1.next()
1
>>> I2 = iter(G)
>>> next(I2)
4
这与某些内置类型(比如list)的行为不同, 它们支持多个迭代器并且在一个活跃迭代器中传递并反映它们地原处修改。
>>> L = [x ** 2 for x in range(4)]
>>> I1 = iter(L)
>>> next(I1)
0
>>> I1.next()
1
>>> I2 = iter(L)
>>> next(I2)
0
>>> del L[2:]
>>> next(I1)
Traceback (most recent call last):
File "", line 1, in
StopIteration