python yield from的意义
制定PEP 380 时,有人质疑作者Greg Ewing 提议的语义过于复杂了。他的回应之一是: “对人类来说,几乎所有最重要的信息都在靠近顶部的某个段落里。”他还引述了 PEP 380 草稿中的一段话,当时那段话是这样的:
“把迭代器当作生成器使用,相当于把子生成器的定义体内联在 yield from 表达式 中。此外,子生成器可以执行 return 语句,返回一个值,而返回的值会成为 yield from 表达式的值。
PEP 380 中已经没有这段宽慰人心的话,因为没有涵盖所有极端情况。不过,一开始可以 这样粗略地说。
批准后的PEP 380 在“Proposal”一节(https://www.python.org/dev/peps/pep-0380/#proposal)
分六点说明了 yield from
的行为。这里,我几乎原封不动地引述,不过把有歧义的“迭代 器”一词都换成了“子生成器”,还做了进一步说明。示例(python 使用yield from https://blog.csdn.net/MZP_man/article/details/100636364)
阐明了下述四点。
1、子生成器产出的值都直接传给委派生成器的调用方(即客户端代码)。
2、使用 send()
方法发给委派生成器的值都直接传给子生成器。如果发送的值是 None
,那 么会调用子生成器的 __next__()
方法。如果发送的值不是 None
,那么会调用子生成器 的 send()
方法。如果调用的方法抛出 StopIteration
异常,那么委派生成器恢复运行。 任何其他异常都会向上冒泡,传给委派生成器。
3、生成器退出时,生成器(或子生成器)中的 return expr
表达式会触发 StopIteration(expr)
异常抛出。
4、yield from
表达式的值是子生成器终止时传给 StopIteration
异常的第一个参数。yield from
结构的另外两个特性与异常和终止有关。
5、传入委派生成器的异常,除了 GeneratorExit
之外都传给子生成器的 throw()
方法。如 果调用 throw()
方法时抛出 StopIteration
异常,委派生成器恢复运行。StopIteration
之外的异常会向上冒泡,传给委派生成器。
6、如果把 GeneratorExit
异常传入委派生成器,或者在委派生成器上调用 close()
方法, 那么在子生成器上调用 close()
方法,如果它有的话。如果调用 close()
方法导致异常 抛出,那么异常会向上冒泡,传给委派生成器;否则,委派生成器抛出 GeneratorExit
异常。
yield from 的具体语义很难理解,尤其是处理异常的那两点。Greg Ewing 做得很好,在 PEP 380 中使用英语阐述了 yield from
的语义。
Ewing 还使用伪代码(使用 Python 句法)演示了 yield from
的行为。我个人认为值得花 时间研究 PEP 380 中的伪代码。不过,那段伪代码长达 40 行,看一遍很难理解。
若想研究那段伪代码,最好将其简化,只涵盖 yield from
最基本且最常见的用法。
假设 yield from
出现在委派生成器中。客户端代码驱动着委派生成器,而委派生成器驱 动着子生成器。那么,为了简化涉及到的逻辑,我们假设客户端没有在委派生成器上调 用 .throw(...)
或 .close()
方法。此外,我们还假设子生成器不会抛出异常,而是一直运 行到终止,让解释器抛出 StopIteration
异常。
个人建议参考官方文档:https://www.python.org/dev/peps/pep-0380/#abstract