Python yield from 用法詳解


本文僅作記錄,原文地址:https://www.jianshu.com/p/87da832730f5

 

 

 

Python yield from 用法詳解

 

 

 

 

yield from是Python3.3新出現的句法

替代內層for循環

如果生成器函數需要產出另一個生成器生成的值,傳統的解決方法是使用嵌套的for循環:

>>> 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 生成器函數把操作依次交給接收到的各個可迭代對象處理。

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

上面這個例子看上去比較簡單(傳統意義上說因為我們只是for循環一次就完事,因為只嵌套了一層),我們再來看幾個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將這個可迭代對象作為一種子例程進行遞歸。最終返回結果就是一個沒有嵌套的單值序列了。
  • 代碼中額外的參數ignore types和檢測語句isinstance(x, ignore types)用來將字符
    串和字節排除在可迭代對象外,防止將它們再展開成單個的字符。
  • 如果這里不用yield from的話,那么就需要另外一個for來嵌套,並不是一種優雅的操作

例子2:利用一個Node類來表示樹結構

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的迭代協議,返回一個迭代器對象,就能迭代了
  • depth_frist返回一個生成器,仔細體會其中的yieldyield from用法

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

打開雙通道

如果 yield from 結構唯一的作用是替代產出值的嵌套 for 循環,這個結構很有可能不會添加到 Python 語言中。yield from 結構的本質作用無法通過簡單的可迭代對象說明,而要發散思維,使用嵌套的生成器。
yield from 的主要功能是打開雙向通道,把最外層的調用方與最內層的子生成器連接起來,這樣二者可以直接發送和產出值,還可以直接傳入異常,而不用在位於中間的協程中添加大量處理異常的樣板代碼。有了這個結構,協程可以通過以前不可能的方式委托職責。
這里有張詳細圖來說明三者關系:http://flupy.org/resources/yield-from.pdf

 
caller,delegating generator and downstream generator

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

 

總結:

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


作者:尋找無雙丶
鏈接:https://www.jianshu.com/p/87da832730f5
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM