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的方式來調用子生成器。
-----未完還在翻譯中。。。