《翻譯》PEP 380 – 委託子生成器語法

 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表達式文法能夠這樣描述:

  • 迭代器yield的任何值都直接傳遞給調用者
  • 使用send()傳遞給委託生成器的值,都直接傳遞到迭代器。若是send()的參數是None,那這個迭代器的__next__()方法會被調用。若是send()的參數不是None,那迭代器的send()方法被調用。若是調用時拋出StopIteration異常,那麼委託生成器被恢復。其餘任何的異常都會傳到委託生成器。
  • 除了GeneratorExit異常外,丟進委託生成器的異常都被傳遞到了迭代器的throw()方法。若是調用時拋出StopIteration異常,委託生成器被恢復。其餘任何的異常都會傳到委託生成器。
  • 若是GeneratorExit異常被丟進委託生成器,或者調用了委託生成器的close()方法,那麼迭代器的close()方法會被調用。若是調用時發生異常,會傳遞到委託生成器。不然,GeneratorExit會在委託生成器拋出。
  • yield from表達式的值是傳遞給StopIteration異常的第一個參數。
  • 在生成器裏,return expr 將致使StopIteration(expr)拋出。

 

加強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()方法的調用。

相比於生成器,子迭代器語法是一個更合理,更通常的作法。

就重構來講,被提議的新語法有下面幾條限制:

  • 捕獲GeneratorExit異常的代碼塊若是沒有在接下來的代碼裏從新拋出異常的話,那麼這塊代碼就不能被分割。儘管保留着一樣的行爲。
  • 若是StopIteration異常被丟進委託生成器,被分割的代碼的行爲可能與未分割的不同。

 

    終結

關於生成器的終結,有一些爭論:在調用close()方法顯示終結委託生成器,當它在yield from掛起時,也應該同時終結子生成器。反對的人認爲,這會致使子生成器過早的被終結,若是這個子生成器被其餘地方引用的話。

考慮到每一個Python的實現版本的差別(非重計數Python),最好仍是顯示的終結生成器,這也保證了被分割的代碼與未分割的代碼在Python的各個版本里有一樣的行爲。

在大多數的使用場景下,子生成器不會被共用。即便存在少數子生成器共用的狀況,子生成器能使用包裹調用throw()和close()的方法容納,或者使用一種替代yield from的方式來調用子生成器。

 

-----未完還在翻譯中。。。

相關文章
相關標籤/搜索