让我们来看一看close/throw调用一个yield from生成器的情况。
close一个yield from生成器,yield from生成器及其子生成器都close掉了,并且下游的子生成器先close,上游的yield from生成器后close。这是显然的,如果上游的yield from生成器先close了,它就没法再去close子生成器了,因为这不是一个异步的过程。
import inspect
def producer():
yield 1
def messenger():
yield from producer()
def consumer():
m = messenger()
next(m)
p = m.gi_yieldfrom
m.close()
return m, p
m, p = consumer()
print(inspect.getgeneratorstate(m))
print(inspect.getgeneratorstate(p))
throw一个非GeneratoExit异常给yield from生成器,yield from生成器会把异常throw给它的子生成器,子生成器未捕获异常,子生成器会异常终止,异常再抛到上游的yield from生成器,yield from未捕获异常也会终止。throw一个GeneratorExit异常的情况稍有不同,yield from生成器会close子生成器然后自己再raise GeneratorExit异常终止自己。
import inspect, traceback
def producer():
yield 1
def messenger():
yield from producer()
def consumer():
m = messenger()
next(m)
p = m.gi_yieldfrom
try:
m.throw(GeneratorExit)
except:
_, _, tb = sys.exc_info()
traceback.print_tb(tb)
return m, p
m, p = consumer()
print(inspect.getgeneratorstate(m))
print(inspect.getgeneratorstate(p))
谈一下yield from递归,它看起来不像普通函数递归那么直观,如果我们把调用过程用一棵树来表示,得到一棵调用树,yield from调用作为中间结点(非叶结点),yield语句是访问结点的动作,执行生成器yield from语句就是由一个结点下降它的一个子结点上,执行生成器的return语句或者后根遍历执行完yield语句,就是由一个结点回溯到它的父结点,所以基于yield from实现树的遍历就很方便了。
下面给出一个基于yield from实现的遍历访问UserList类型树的算法。先给出类型树。
再给出代码:
from collections import UserList
def rootfirst(cls):
yield cls
for i in cls.__bases__:
yield from rootfirst(i)
for i in rootfirst(UserList):
print(i)
def rootlast(cls):
for i in cls.__bases__:
yield from rootlast(i)
yield cls
for i in rootlast(UserList):
print(i)