小知识,大挑战!本文正在参与「程序员必备小知识」创作活动
先前我们介绍了协程send函数的使用。
除了send之外,python还添加了一些有助于协程使用的功能。比如yield from 这个表达式。
它长这样:
yield from xxxx
xxx是可迭代对象。 从最简单的意义上来讲,它的效果相当于这样:
for i in xxxx:
yield i
也就是把整个xxxx这个可迭代对象中的每一项都生成出去。 举个例子
def gen():
yield from range(5)
yield 2
yield from range(5)
# [0, 1, 2, 3, 4, 2, 0, 1, 2, 3, 4] 使用list(gen(n))会得到 这样的结果
所有的通过yield from 调用迭代器的结果,都会像先前写的for循环一样被当作gen函数产出的值,交由外部使用。
不过我们先前也说了,这个东西实际上是为协程准备的。
yield from 的后面是可以写一个协程的,当然协程或者说生成器本身就是迭代器,所以这是肯定的。
但并不只是这样,真正的好处在于它可以起到一种中介的作用。以上面的函数为例,对于外部,yield from 的值可以被当作gen函数的产出值,而反过来,对于yield from后的协程函数,外部的操作可以被转接到内层生成器上。
我们换一个例子,假设使用我们之前写过的统计奇数和偶数的协程, 然后通过yield from使用
def count():
even = 0
odd = 0
while True:
cur = yield even, odd
print("#",cur)
if cur % 2 == 0:
even += 1
else:
odd += 1
def gen():
yield from count()
像先前一样测试一下
corutine = gen()
next(corutine)
while True:
x = int(input())
print(corutine.send(x))
可以发现的是,协程可以以相同的效果执行。在yield from表达式运行时,可以当作我们就是直接对后面的协程操作。
如果我们想手动做这些操作的话,其实并不一定很好做,姑且先只考虑最简单的send 转发,可能得像下面这么写
def gen():
cur = yield
sub = count()
try:
while True:
cur = yield sub.send(cur)
except:
pass
但是,更令人麻烦的是,实际上协程还有其他的操作函数,像throw之类的。
而yeild from 正是为我们解决这些痛点,当我们想要把一些操作抽取到另一个方法中,但是调用就需要舍弃特性,或者手动的做中介处理。
而yeild from避免了这种情况。它可以让我们的代码就好像是内联在原来的外部协程上一样,不用太多心智负担。