Fluent Python 讀書筆記(五)
控制流程
- 在大部分情況下,Python 社群將 Iterator 與 Generator 視為同義詞
- Python 所有集合都是可迭代的
- 內部的 for 迴圈、集合生成式、變數和引數的 Unpacking 都會用到 Iterator
iter()
會先參考__iter__
,其次才參考__getitem__
,都沒有的話,發出 TypeError 代表「該物件不可迭代」(此處__getitem__
的參考在以後可能被棄用)- 承上,可迭代物件不一定滿足
isinstance(C, abc.Iterable)
(在未實作__iter__
的情況下),為了避免這個誤區,要判斷物件是否可迭代,最準確的方式是呼叫iter()
看看 - 如果 iter() 會過,那物件是「Iterable」;實作
__iter__
,須回傳一個「Iterator 實體」—— Python 會跟 Iterable 索取 Iterator - Iterator 類別的標準介面:
__iter__
跟__next__
,__next__
負責回傳下一個項目或發起 StopIteration,__iter__
則單純回傳self
- 不要把 Iterable 跟 Iterator 混為一談,「Iterable 有一個
__iter__
方法,這個方法每次都會實例化一個新的 Iterator」 - Iterator 也是 Iterable,但 Iterable 不是 Iterator。Iterable 永遠不該扮演自己的 Iterator
- Iterator 獨立出來的用意是「每一個迭代器都能保存它自己的內部狀態」
- 除了回傳獨立的 Iterator 實體,也可以將
__iter__
變成一個「Generator 函式」,藉由回傳一個「Generator 實體」,以介面而言,Generator 是 Iterator,它會在內文結束時發出 StopIteration - 用一個「 lazy 的產生器」取代一個「儲存所有資料的迭代器實體」是更好的,因為只要在必要時(最後一刻)才產生值,可以節省大量記憶體
- Iterator 的另一個功能是「延緩工作」、「一次只產生一個項目」
- 「當你在用 Python3 想著『有更 lazy 的作法嗎?』的時候,答案通常都是『有』」
yield from
不只是一個糖衣語法,除了取代迴圈之外,它也是一個管道,連接外部產生器,接收外部產生器的值all
、any
有一種重要的優化是reduce
無法作到的,那就是 short-circuit,確定結果後就停止sorted
可以接收任意的 Iterableiter()
的另一個功能:傳入一個 Callable 及一個標記值(sentinel),當回傳值等同此標記時,停止迭代- 無論資料大小為何,Generator 提供一種有彈性的解決方案,把大型資料集當做資料流來處理
.send()
同樣會讓產生器進入下一個yield
,但是它也可以用來傳入資料,相較於next()
單純接收資料,.send()
可讓使用者與產生器雙向交換資料——變成協同程序 (coroutines)- 「在內文埋入一個 yield,不足以提醒那一個語意有如此不同」(但 Guido 討厭使用新的關鍵字)
- 以實作而言,Generator 是一種語言結構,以函式或表達式編寫,呼叫時回傳
GeneratorType
- 以概念而言,不管 Iterator 內部有多複雜(例如是一個樹狀資料結構),它的資料永遠只有一個來源(自己本身);至於產生器,則不一定只產生集合裡面的項目
- 「Iterator 最簡單的介面是由 First、Next、IsDone、CurrentItem 的操作組成」,在 Python 它的介面更精簡:
next()
跟StopIteration