Python生成器

内容绝大部分出自《Python高级编程》,Luke Sneeringer,清华大学出版社,Python版本2.7。
代码部分经修改可以完整运行,方便理解和直接测试。

生成器只在需要时才计算序列中的值,节省了内存空间。
生成器是一个函数,单次执行,直到迭代终止,可以表示无限序列(通过一个while true语句)。
生成器通常是通过yield语句实现。yield不会终止函数执行,而是暂停函数,当调用next或者send方法触发生成器时,生成器则从暂停位置继续执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#!/usr/bin/env python
#_*_ coding:utf-8 _*_
#无限斐波那契数列,如果你像我一样无聊的话,可以
#for i in fibonacci():print i 试一下刷屏的快感
def fibonacci():
numbers = []
while True:
#序列初始化,list里少于两个数就扔个1进去
if len(numbers) < 2:
numbers.append(1)
else:
#求出下一个值
numbers.append(sum(numbers))
#弹出前一个,保证numbers中只有两个值
numbers.pop(0)
#每次输出序列尾部的值,程序顺序执行,当遇到的第一个yield语句后,输出yield语句
#输出值,然后就暂停了,yield之后的语句(该处为下次循环)直到下次用next者send
#启动生成器才会执行(详见下一个例子)
yield numbers[-1]
#赋值指定两个生成器
f = fibonacci()
a = fibonacci()
#查看类型:generator,可以看出生成器还没有初始化,所以没有产生数值
print type(f)
#查看方法,我们只关心next,send,throw
print dir(f)
#这句与f.next()效果等同,都是相当于初始化生成器
#但是初始化生成器时,不能send一个no-None的值,否则会报出
#TypeError: can't send non-None value to a just-started generator
print f.send(None)
#连续输出几个值看看
for i in range(10):
print f.next(),
print
#next不接受参数,否则会报
#TypeError: expected 0 arguments, got 1
print f.next()
#效果和上面一样
print f.send(None)
#初始化后就可以随便send了(由于我们在生成器中没有接收传递进来的值,所以send什么都一样)
print f.send(1)
#这句输出看得出来a,f是分开的
print a.next()
print f.next()

尽量写了个能看出yield语句暂停位置的东西:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/usr/bin/env python
#_*_ coding:utf-8 _*_
j = 1
def fibonacci():
numbers = []
global j
while True:
if len(numbers) < 2:
numbers.append(1)
else:
numbers.append(sum(numbers))
last = numbers.pop(0)
j += 1
print 'Before yield: ',
print [i for i in numbers[:]],'yield output:',
yield numbers[-1]
print 'After yield:',
print [i for i in numbers[:]]
#print 'last number is %s' % numbers[-1]
f = fibonacci()
for i in range(5):
print '\nOutput %s ' % j
print f.next()

输出如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Output 1
Before yield: [1] yield output: 1
Output 2
After yield: [1]
Before yield: [1, 1] yield output: 1
Output 3
After yield: [1, 1]
Before yield: [1, 2] yield output: 2
Output 4
After yield: [1, 2]
Before yield: [2, 3] yield output: 3
Output 5
After yield: [2, 3]
Before yield: [3, 5] yield output: 5