流暢的python第十四章可迭代的對象,迭代器和產生器學習記錄

來源:互聯網
上載者:User

標籤:內建函數   需要   子類   推導   ati   最好   說明   top   類型   

在python中,所有集合都可以迭代,在python語言內部,迭代器用於支援

for迴圈

構建和擴充集合類型

逐行遍曆文字檔

列表推導,字典推導和集合推導

元組拆包

調用函數時,使用*拆包實參

本章涵蓋的話題

語言內部使用 iter(...) 內建函數處理可迭代對象的方式
如何使用 Python 實現經典的迭代器模式
詳細說明產生器函數的工作原理
如何使用產生器函數或產生器運算式代替經典的迭代器
如何使用標準庫中通用的產生器函數
如何使用 yield from 語句合并產生器
案例分析:在一個資料庫轉換工具中使用產生器函數處理大型資料集
為什麼產生器和協程看似相同,實則差別很大,不能混淆

 

序列可以迭代的原因:iter函數

解譯器需要迭代對象 x 時,會自動調用 iter(x)。
內建的 iter 函數有以下作用。
(1) 檢查對象是否實現了 __iter__ 方法,如果實現了就調用它,擷取一個迭代器。
(2) 如果沒有實現 __iter__ 方法,但是實現了 __getitem__ 方法,Python 會建立一個迭
代器,嘗試按順序(從索引 0 開始)擷取元素。
(3) 如果嘗試失敗,Python 拋出 TypeError 異常,通常會提示“C object is not iterable”(C
對象不可迭代),其中 C 是目標對象所屬的類。

從 Python 3.4 開始,檢查對象 x 能否迭代,最準確的方法是:調用 iter(x) 函
數,如果不可迭代,再處理 TypeError 異常。這比使用 isinstance(x,
abc.Iterable) 更準確,因為 iter(x) 函數會考慮到遺留的 __getitem__ 方法,
而 abc.Iterable 類則不考慮。

 

可迭代的對象與迭代器的對比

可迭代的對象
  使用 iter 內建函數可以擷取迭代器的對象。如果對象實現了能返回迭代器的
__iter__ 方法,那麼對象就是可迭代的。序列都可以迭代;實現了 __getitem__ 方
法,而且其參數是從零開始的索引,這種對象也可以迭代

>>> s = ‘ABC‘>>> it = iter(s) # ?>>> while True:...     try:...         print(next(it)) # ?...     except StopIteration: # ?...         del it # ?...         break # ?...A
B
C

? 使用可迭代的對象構建迭代器 it。
? 不斷在迭代器上調用 next 函數,擷取下一個字元。
? 如果沒有字元了,迭代器會拋出 StopIteration 異常。
? 釋放對 it 的引用,即廢棄迭代器對象。
? 退出迴圈。

StopIteration 異常表明迭代器到頭了。Python 語言內部會處理 for 迴圈和其他迭代上
下文(如列表推導、元組拆包,等等)中的 StopIteration 異常。

標準的迭代器介面有兩個方法。
__next__
  返回下一個可用的元素,如果沒有元素了,拋出 StopIteration 異常。
__iter__
  返回 self,以便在應該使用可迭代對象的地方使用迭代器,例如在 for 迴圈中。

在 Python 3 中,Iterator 抽象基類定義的抽象方法是 it.__next__(),而
在 Python 2 中是 it.next()。一如既往,我們應該避免直接調用特殊方法,使用
next(it) 即可,這個內建的函數在 Python 2 和 Python 3 中都能使用。

查對象 x 是否為迭代器最好的方式是調用 isinstance(x, abc.Iterator)。得益

於 Iterator.__subclasshook__ 方法,即使對象 x 所屬的類不是 Iterator 類的
真實子類或虛擬子類,也能這樣檢查

因為迭代器只需 __next__ 和 __iter__ 兩個方法,所以除了調用 next() 方法,以及捕
獲 StopIteration 異常之外,沒有辦法檢查是否還有遺留的元素。此外,也沒有辦
法“還原”迭代器。如果想再次迭代,那就要調用 iter(...),傳入之前構建迭代器的可
迭代對象。傳入迭代器本身沒用,因為前面說過 Iterator.__iter__ 方法的實現方式是
返回執行個體本身,所以傳入迭代器無法還原已經耗盡的迭代器。

迭代器
  迭代器是這樣的對象:實現了無參數的 __next__ 方法,返回序列中的下一個元素;
如果沒有元素了,那麼拋出 StopIteration 異常。Python 中的迭代器還實現了
__iter__ 方法,因此迭代器也可以迭代。

 

構建可迭代的對象和迭代器時經常會出現錯誤,原因是混淆了二者。要知道,可迭代的對
象有個 __iter__ 方法,每次都執行個體化一個新的迭代器;而迭代器要實現 __next__ 方
法,返回單個元素,此外還要實現 __iter__ 方法,返回迭代器本身。

因此,迭代器可以迭代,但是可迭代的對象不是迭代器。

可迭代的對象一定不能是自身的迭代器。也就是說,可迭代的對象必須實現
__iter__ 方法,但不能實現 __next__ 方法。

另一方面,迭代器應該一直可以迭代。迭代器的 __iter__ 方法應該返回自身。

產生器函數的工作原理
只要 Python 函數的定義體中有 yield 關鍵字,該函數就是產生器函數。調用產生器函數
時,會返回一個產生器對象。也就是說,產生器函數是產生器工廠。

產生器函數會建立一個產生器對象,封裝產生器函數的定義體。把產生器傳給
next(...) 函數時,產生器函數會向前,執行函數定義體中的下一個 yield 語句,返回
產出的值,並在函數定義體的當前位置暫停。最終,函數的定義體返回時,外層的產生器
對象會拋出 StopIteration 異常——這一點與迭代器協議一致。

何時使用產生器運算式

產生器運算式是建立產生器的簡潔句法,這樣無需先定義函數再調
用。不過,產生器函數靈活得多,可以使用多個語句實現複雜的邏輯,也可以作為協程
使用

選擇使用哪種句法很容易判斷:如果產生器運算式要分成多行寫,我傾向
於定義產生器函數,以便提高可讀性。此外,產生器函數有名稱,因此可以重用。

使用itertools模組產生等差數列
Python 3.4 中的 itertools 模組提供了 19 個產生器函數,結合起來使用能實現很多有趣
的用法。

itertools.count 函數返回的產生器能產生多個數。如果不傳入參
數,itertools.count 函數會產生從零開始的整數數列。不過,我們可以提供可選的
start 和 step 值

不過,itertools.takewhile 函數則不同,它會產生一個使用另一個產生器的產生器,
在指定的條件計算結果為 False 時停止。因此,可以把這兩個函數結合在一起使用

標準庫中的產生器函數

把產生器當成協程

與 .__next__() 方法一樣,.send() 方法致使產生器前進到下一個 yield 語句。不
過,.send() 方法還允許使用產生器的客戶把資料發給自己,即不管傳給 .send() 方法
什麼參數,那個參數都會成為產生器函數定義體中對應的 yield 運算式的值。也就是
說,.send() 方法允許在客戶代碼和產生器之間雙向交換資料。而 .__next__() 方法只
允許客戶從產生器中擷取資料。

 

流暢的python第十四章可迭代的對象,迭代器和產生器學習記錄

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.