生成器是一种特殊的迭代器
1. 如何构造一个生成器
In [6]: li = [item for item in range(10)]
...: print(li)
...: ge = (item for item in range(10))
...: print(ge)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
<generator object <genexpr> at 0x7fee50aaf048>
可以看到上面的创建方式的不同来自于[ ] ( ),但创建出来的一个是列表,一个是生成器,我们可以使用next()方法和for循环来调用生成器
In [7]: ge = (item for item in range(4))
...: print(next(ge))
...: print(next(ge))
...: print(next(ge))
...: print(next(ge))
...: print(next(ge))
0
1
2
3
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-7-af4a652793c7> in <module>
4 print(next(ge))
5 print(next(ge))
----> 6 print(next(ge))
StopIteration:
In [8]: ge = (item for item in range(4))
...: for i in ge:
...: print(i)
...:
0
1
2
3
2. 构造生成器的另一种方式
首先我们回顾下迭代器,前面说过生成器是一种特殊的迭代器
首先我们先使用迭代器实现斐波那契数列,然后在使用生成器来实现,来比较两者的不同
使用迭代器来实现
In [9]: class Fibo(object):
...: def __init__(self, count):
...: self.start = 0
...: self.end = 1
...: self.current = 0
...: self.count = count
...:
...: def __next__(self):
...: if self.current <= self.count:
...: fibo = self.start
...: self.start, self.end = self.end, self.end+self.start
...: self.current += 1
...: return fibo
...: else:
...: raise StopIteration
...:
...: def __iter__(self):
...: return self
...:
...: fibo = Fibo(10)
...: for i in fibo:
...: print(i)
...:
0
1
1
2
3
5
8
13
21
34
55
使用生成器来实现
In [10]: def Fibo(n):
...: count = 0
...: start, end = 0, 1
...: while count <= n:
...: fibo = start
...: start, end = end, start+end
...: count += 1
...: yield fibo
...: return 'None'
...:
...: fibo = Fibo(10)
...: for i in fibo:
...: print(i)
...:
0
1
1
2
3
5
8
13
21
34
55
3. 小结
- 函数中使用了yield关键字的都是生成器
- yield关键字的作用
- yield起到了return的作用,返回它后面的值
- yield保存了当前运行状态,暂停执行,也就是将函数挂起
- 使用next()将函数唤醒,继续执行
- python3中使用yield最终可以使用return来返回一个值,这个值需要捕获StopIteration,python2中不允许使用return返回一个返回值
4. 使用send()来唤醒
除了使用next()函数来唤醒生成器之外,send()函数也可以唤醒。使用send()函数的一个好处是可以向断点传入数据。
In [11]: def gene():
...: count = 0
...: while count < 5:
...: yield count
...: count += 1
...: g = gene()
...: print(next(g))
...: print(next(g))
...: print(next(g))
...: print(next(g))
...: print(next(g))
...: print(next(g))
0
1
2
3
4
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-11-fff2af188c57> in <module>
10 print(next(g))
11 print(next(g))
---> 12 print(next(g))
StopIteration:
In [12]: def gene():
...: count = 0
...: while count < 5:
...: temp = yield count
...: print(temp)
...: count += 1
...:
...: g = gene()
...: print(next(g))
...: print(g.send("a"))
...: print(g.send("b"))
...: print(g.send("c"))
...: print(g.send("d"))
...: print(g.send("e"))
...: print(g.send("f"))
0
a
1
b
2
c
3
d
4
e
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-12-f42c13492dca> in <module>
12 print(g.send("c"))
13 print(g.send("d"))
---> 14 print(g.send("e"))
15 print(g.send("f"))
StopIteration:
使用send()函数来向temp变量来传递一个值,生成器在第一次执行时,碰到yield会挂起,而这时send()函数传递的值没有变量去接收,这就会报错,所以第一次的执行应该是next()函数,如果send()函数为第一次执行,那么,send()传递的变量应该为None。
In [13]: def gene():
...: count = 0
...: while count < 3:
...: temp = yield count
...: print(temp)
...: count += 1
...:
...: g = gene()
...: print(g.send("a")) # 第一次使用send()来执行
...: print(g.send("b"))
...: print(g.send("c"))
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-13-8a4a1728c567> in <module>
7
8 g = gene()
----> 9 print(g.send("a")) # 第一次使用send()来执行
10 print(g.send("b"))
11 print(g.send("c"))
TypeError: can't send non-None value to a just-started generator
In [14]: def gene():
...: count = 0
...: while count < 3:
...: temp = yield count
...: print(temp)
...: count += 1
...:
...: g = gene()
...: print(g.send(None)) # 第一次使用send()来执行,但是传递的参数是None
...: print(g.send("b"))
...: print(g.send("c"))
0
b
1
c
2