Python yield from 用法詳解

yield from是Python3.3新出現(xiàn)的句法

替代內(nèi)層for循環(huán)

如果生成器函數(shù)需要產(chǎn)出另一個生成器生成的值么库,傳統(tǒng)的解決方法是使用嵌套的for循環(huán):

>>> def chain(*iterables):
...     for it in iterables:
...         for i in it:
...             yield i
>>> s = 'ABC'
>>> t = tuple(range(3))
>>> list(chain(s, t))
['A', 'B', 'C', 0, 1, 2]

chain 生成器函數(shù)把操作依次交給接收到的各個可迭代對象處理酷愧。

Python3.3之后引入了新語法:
>>> def chain(*iterables):
...     for i in iterables:
...         yield from i
...
>>> list(chain(s, t))
['A', 'B', 'C', 0, 1, 2]
  • yield from 完全代替了內(nèi)層的 for 循環(huán)。
  • yield from x 表達式對 x 對象所做的第一件事是,調(diào)用 iter(x)履植,從中獲取迭代器勉耀。因
    此,x 可以是任何可迭代的對象仗阅。
  • 在這個示例中使用 yield from代碼讀起來更順暢,不過感覺更像是語法糖国夜。

上面這個例子看上去比較簡單(傳統(tǒng)意義上說因為我們只是for循環(huán)一次就完事减噪,因為只嵌套了一層),我們再來看幾個yield from的例子车吹。
例子1:我們有一個嵌套型的序列筹裕,想將它扁平化處理為一列單獨的值。

from collections import Iterable

def flatten(items, ignore_types=(str, bytes)):
    for x in items:
        if isinstance(x, Iterable) and not isinstance(x, ignore_types):
            yield from flatten(x)
        else:
            yield x

items = [1, 2, [3, 4, [5, 6], 7], 8]
for x in flatten(items):
    print(x)
# output:
 1 2 3 4 5 6 7 8
-----------------------------------------------
items = ['Dave', 'Paula', ['Thomas', 'Lewis']]
for x in flatten(items):
    print(x)

# output:
Dave
Paula
Thomas
Lewis
  • collections.Iterable是一個抽象基類窄驹,我們用isinstance(x, Iterable)檢查某個元素是否是可迭代的.如果是的話,那么就用yield from將這個可迭代對象作為一種子例程進行遞歸朝卒。最終返回結(jié)果就是一個沒有嵌套的單值序列了。
  • 代碼中額外的參數(shù)ignore types和檢測語句isinstance(x, ignore types)用來將字符
    串和字節(jié)排除在可迭代對象外乐埠,防止將它們再展開成單個的字符抗斤。
  • 如果這里不用yield from的話,那么就需要另外一個for來嵌套丈咐,并不是一種優(yōu)雅的操作

例子2:利用一個Node類來表示樹結(jié)構(gòu)

class Node:
    def __init__(self, value):
        self._value = value
        self._children = []

    def __repr__(self):
        return 'Node({!r})'.format(self._value)

    def add_child(self, node):
        self._children.append(node)

    def __iter__(self):
        return iter(self._children)

    def depth_first(self):
        yield self
        for c in self:
            yield from c.depth_first()


if __name__ == '__main__':
    root = Node(0)
    child1 = Node(1)
    child2 = Node(2)
    root.add_child(child1)
    root.add_child(child2)
    child1.add_child(Node(3))
    child1.add_child(Node(4))
    child2.add_child(Node(5))
    for ch in root.depth_first():
        print(ch)
  • __iter__代表一個Pyton的迭代協(xié)議瑞眼,返回一個迭代器對象,就能迭代了
  • depth_frist返回一個生成器,仔細體會其中的yieldyield from用法

上面兩個例子無論是樹還是嵌套序列棵逊,都比較復雜伤疙,觀察這里yield from跟的是什么,跟的是函數(shù),生成器函數(shù),而且都是在函數(shù)內(nèi)遞歸。雖然我也不是理解的很透徹 =,= 辆影。但現(xiàn)在應該知道掩浙,這是yield from一種常用的方法了(認真體會,手動滑稽)秸歧。

打開雙通道

如果 yield from 結(jié)構(gòu)唯一的作用是替代產(chǎn)出值的嵌套 for 循環(huán)厨姚,這個結(jié)構(gòu)很有可能不會添加到 Python 語言中。yield from 結(jié)構(gòu)的本質(zhì)作用無法通過簡單的可迭代對象說明键菱,而要發(fā)散思維谬墙,使用嵌套的生成器今布。
yield from 的主要功能是打開雙向通道,把最外層的調(diào)用方與最內(nèi)層的子生成器連接起來拭抬,這樣二者可以直接發(fā)送和產(chǎn)出值部默,還可以直接傳入異常,而不用在位于中間的協(xié)程中添加大量處理異常的樣板代碼造虎。有了這個結(jié)構(gòu)傅蹂,協(xié)程可以通過以前不可能的方式委托職責。
這里有張詳細圖來說明三者關系:http://flupy.org/resources/yield-from.pdf

caller,delegating generator and downstream generator

這里只是大概展示下三者關系算凿,具體的內(nèi)容可以去上面那個網(wǎng)址仔細觀察份蝴。
例子就不展開了,有興趣的童鞋可以去 Fluent Python這本書上 查看 示例16-17氓轰。并且結(jié)合示例圖好好體會(我也有待好好體會)

總結(jié):

  1. yield from常用來代替內(nèi)層for循環(huán) 與 打開雙通道
  2. 但是大部分情況下yield from并不單獨使用婚夫,而是伴隨著asyncio庫使用,實現(xiàn)異步操作(一異步操作后面講)
  3. 從Python 3.5開始引入了新的語法 asyncawait 署鸡,而await替代的就是yield from(為了不與實現(xiàn)內(nèi)層for循環(huán)的yield from誤解)

參考資料

David beazley協(xié)程
Fluent Python
Python Cookbook

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末案糙,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子靴庆,更是在濱河造成了極大的恐慌时捌,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,692評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件炉抒,死亡現(xiàn)場離奇詭異奢讨,居然都是意外死亡,警方通過查閱死者的電腦和手機端礼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評論 3 392
  • 文/潘曉璐 我一進店門禽笑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來入录,“玉大人蛤奥,你說我怎么就攤上這事×鸥澹” “怎么了凡桥?”我有些...
    開封第一講書人閱讀 162,995評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長蚀同。 經(jīng)常有香客問我缅刽,道長,這世上最難降的妖魔是什么蠢络? 我笑而不...
    開封第一講書人閱讀 58,223評論 1 292
  • 正文 為了忘掉前任衰猛,我火速辦了婚禮,結(jié)果婚禮上刹孔,老公的妹妹穿的比我還像新娘啡省。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 67,245評論 6 388
  • 文/花漫 我一把揭開白布卦睹。 她就那樣靜靜地躺著畦戒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪结序。 梳的紋絲不亂的頭發(fā)上障斋,一...
    開封第一講書人閱讀 51,208評論 1 299
  • 那天,我揣著相機與錄音徐鹤,去河邊找鬼垃环。 笑死,一個胖子當著我的面吹牛凳干,可吹牛的內(nèi)容都是我干的晴裹。 我是一名探鬼主播,決...
    沈念sama閱讀 40,091評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼救赐,長吁一口氣:“原來是場噩夢啊……” “哼涧团!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起经磅,我...
    開封第一講書人閱讀 38,929評論 0 274
  • 序言:老撾萬榮一對情侶失蹤泌绣,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后预厌,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體阿迈,經(jīng)...
    沈念sama閱讀 45,346評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,570評論 2 333
  • 正文 我和宋清朗相戀三年轧叽,在試婚紗的時候發(fā)現(xiàn)自己被綠了苗沧。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,739評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡炭晒,死狀恐怖待逞,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情网严,我是刑警寧澤识樱,帶...
    沈念sama閱讀 35,437評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站震束,受9級特大地震影響怜庸,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜垢村,卻給世界環(huán)境...
    茶點故事閱讀 41,037評論 3 326
  • 文/蒙蒙 一割疾、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧嘉栓,春花似錦宏榕、人聲如沸驰凛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽恰响。三九已至,卻和暖如春涌献,著一層夾襖步出監(jiān)牢的瞬間胚宦,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評論 1 269
  • 我被黑心中介騙來泰國打工燕垃, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留枢劝,地道東北人。 一個月前我還...
    沈念sama閱讀 47,760評論 2 369
  • 正文 我出身青樓卜壕,卻偏偏與公主長得像您旁,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子轴捎,可洞房花燭夜當晚...
    茶點故事閱讀 44,647評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 把函數(shù)視為對象:一等函數(shù) 把函數(shù)視為對象 python函數(shù)是對象鹤盒。 高階函數(shù) 函數(shù)式編程的特點之一是使用高階函數(shù)。...
    plutoese閱讀 1,462評論 0 52
  • 從語法上來看侦副,協(xié)程和生成器類似侦锯,都是定義體中包含yield關鍵字的函數(shù)。yield在協(xié)程中的用法:在協(xié)程中yiel...
    JokerW閱讀 1,803評論 0 0
  • 你不知道JS:異步 第四章:生成器(Generators) 在第二章秦驯,我們明確了采用回調(diào)表示異步流的兩個關鍵缺點:...
    purple_force閱讀 957評論 0 2
  • PYTHON-進階-ITERTOOLS模塊小結(jié)轉(zhuǎn)自wklken:http://wklken.me/posts/20...
    C_Y_閱讀 968評論 0 2
  • 山高林密彩云南尺碰, 風疾雪厚藏高原。 誰道蜀人多悠閑译隘, 枕戈待旦衛(wèi)長天亲桥。
    成都獨行俠閱讀 545評論 0 2