PEP 380 – 委托子生成器语法html
翻译自: https://www.python.org/dev/peps/pep-0380/python
摘要函数
一项新的语法被提出了:生成器委托其部分操做给另外一个生成器。委托也就意味着包含’yield’的那部分代码可能被分解,而且放置在另外一个生成器里。此外,子生成器能够返回一个值,并且这个值对于委托生成器可用(即上层的生成器)。优化
同时,当一个生成器重复屡次的yield另外一个生成器产生的值时,能够经过这项新的语法进行优化。ui
PEP授理 Guido 正式接受这项 PEP [1] 于 26日 6月, 2011.spa
目的翻译
Python的生成器是协程的一种表现形式,但它有局限性,只能向它的直接调用者yield。也就是说,包含yield语句的代码片断不能被分解,也不能放在单独的函数里。这么作致使了被调用的函数变成了一个生成器,而且有必要明确地迭代第二个生成器,从新re-yield值。code
若是只考虑yield的值,可使用这样的循环:协程
for v in g: yield v
然而,若是子生成器要适当地与调用者进行交互,好比调用send(),throw()和close()方法时,就会变得至关复杂。稍后会看到,要写的代码至关复杂,处理边界状况也是用尽心机。htm
为解决这个问题,一项新的语法被提出来。在最简单的状况下,它的使用等同于for循环,同时它也会处理原有的生成器全部行为,以一种简单直接的方式让生成器代码能够被分解。
提议
能够将下列的新语法应用在生成器内:
yield from <expr>
其中,<expr>是一个可迭代对象(iterable),能够从中提取出迭代器。这个迭代器会从调用者的生成器yield值和接收值,直到耗尽。
此外,若是这个迭代器是另外一个生成器的话,子生成器可使用return返回一个值,这个值就做为yield from语句的返回值。
根据生成器协议,yield from表达式文法能够这样描述:
加强StopIteration
为了方便使用,StopIteration异常将有一个value属性,它的值是建立时传入的第一个参数。
正式语义
python 3 语法使用以下.
1.表达式
RESULT = yield from EXPR
在语义上等同于:
_i = iter(EXPR) try: _y = next(_i) except StopIteration as _e: _r = _e.value else: while 1: try: _s = yield _y except GeneratorExit as _e: try: _m = _i.close except AttributeError: pass else: _m() raise _e except BaseException as _e: _x = sys.exc_info() try: _m = _i.throw except AttributeError: raise _e else: try: _y = _m(*_x) except StopIteration as _e: _r = _e.value break else: try: if _s is None: _y = next(_i) else: _y = _i.send(_s) except StopIteration as _e: _r = _e.value break RESULT = _r
2.在一个生成器内,表达式
return value
在语义上等同于
raise StopIteration(value)
除了不能被except捕获的异常
1.StopIteration异常表现以下:
class StopIteration(Exception): def __init__(self, *args): if len(args) > 0: self.value = args[0] else: self.value = None Exception.__init__(self, *args)
理论
重构原则
上面说的这些语义,目的是可以重构生成器代码。应该让一部分代码包含有一个或多个yield表达式,分到独自的函数里(也就是使用常规的技巧处理做用域的变量引用),最终经过使用yield from表达式来调用新的函数。
在任何场景下,合成生成器应该与最初的,没分割的生成器行为一致,这就包括__next__(),send(),throw()和close()方法的调用。
相比于生成器,子迭代器语法是一个更合理,更通常的作法。
就重构来讲,被提议的新语法有下面几条限制:
终结
关于生成器的终结,有一些争论:在调用close()方法显示终结委托生成器,当它在yield from挂起时,也应该同时终结子生成器。反对的人认为,这会致使子生成器过早的被终结,若是这个子生成器被其余地方引用的话。
考虑到每一个Python的实现版本的差别(非重计数Python),最好仍是显示的终结生成器,这也保证了被分割的代码与未分割的代码在Python的各个版本里有一样的行为。
在大多数的使用场景下,子生成器不会被共用。即便存在少数子生成器共用的状况,子生成器能使用包裹调用throw()和close()的方法容纳,或者使用一种替代yield from的方式来调用子生成器。
-----未完还在翻译中。。。