本文首发于
知乎
yield生成器在python中使用广泛,更是python中协程的实现原理,有必要深入掌握。
本文分为如下几个部分
- 简单yield的使用
- yield空
- yield from
- send与yield赋值
- return yield
简单yield的使用
下面是一个最简单的yield使用的例子
def myfun(total):
for i in range(total):
yield i
定义了
myfun
函数,调用时比如
myfun(4)
得到的是一个生成器,生成器有3种调用方式
1.用next调用
>>> m = myfun(4)
>>> next(m)
0
>>> next(m)
1
>>> next(m)
2
>>> next(m)
3
>>> next(m)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
每次调用一次
next
,函数都会执行到
yield
处停止,等待下一次
next
,
next
次数超出限制就会抛出异常
2.循环调用
for i in myfun(4):
print(i)
运行结果为
0
1
2
3
生成器是可迭代对象,可以用循环调用。循环调用就是最大限度地调用
next
,并返回每次
next
运行结果
这就是yield的最基本使用,下面来应用一下
python内置模块itertools中实现了一些小的生成器,我们这里拿
count
举例子,要实现下面这样的效果
>>
> from itertools import count
>>> c = count(start = 5, step = 2)
>>> next(c)
5
>>> next(c)
7
>>> next(c)
9
>>> next(c)
11
......
实现如下
def count(start, step = 1):
n = 0
while True:
yield start + n
n += step
3.循环中调用
next
下面循环中调用
next
,用
StopIteration
捕获异常并退出循环
>>> m = myfun(4)
>>> while True:
... try:
... print(next(m))
... except StopIteration:
... break
...
0
1
2
3
yield空
yield空相当于一个中断器,循环运行到这里会中断,用于辅助其他程序的执行。也可以理解成返回值是
None
,我们来看下面这个例子
>>> def myfun(total):
... for i in range(total):
... print(i + 1)
... yield
...
>>> a = myfun(3)
>>> next(a)
1
>>> next(a)
2
>>> next(a)
3
>>> next(a)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>> a = myfun(3)
>>> for i in a:
... print(i)
...
1
None
2
None
3
None
yield from
通过下面一个例子来展示
yield from
的用法
def myfun(total):
for i in range(total):
yield i
yield from ['a', 'b', 'c']
用
next
调用结果如下
>>> m = myfun(3)
>>> next(m)
0
>>> next(m)
1
>>> next(m)
2
>>> next(m)
'a'
>>> next(m)
'b'
>>> next(m)
'c'
>>> next(m)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
上面的函数相当于
def myfun(total):
for i in range(total):
yield i
for i in ['a', 'b', 'c']:
yield i
下面我们也做一个小练习,实现itertools模块中的
cycle
,效果如下
>>> from itertools import cycle
>>> a = cycle('abc')
>>> next(a)
'a'
>>> next(a)
'b'
>>> next(a)
'c'
>>> next(a)
'a'
>>> next(a)
'b'
>>> next(a)
'c'
>>> next(a)
'a'
实现如下
def cycle(p):
yield from p
yield from cycle(p)
send与yield赋值
先讲
send
,首先明确一点,
next
相当于
send(None)
,还是看下面最简单的例子
>>> def myfun(total):
... for i in range(total):
... yield