我們在面試一份工作,面試官要求從代碼塊中刪除所有的for循環。然後他們提到一些關於迭代器的觀點同時手指敲打在桌子上並瘋狂地大笑。我們很緊張,並且對被分配這個可笑的任務感到沮喪,但是我們將盡最大的努力。
為了理解不用for循環如何循環,我們需要找出for是怎麼運作的。
我們要學習在Python中for循環如何工作。在這個過程中,我們需要了解可迭代對象,迭代器和迭代協議。
使用下標循環:一次失敗的嘗試我們可能首先嘗試通過使用來自於C的傳統循環風格來刪除for循環:使用下標循環。
這對list有效,但在set上行不通:
這種方法只適用於序列,它是含有索引從0到比序列長度小一的數據類型。列表、字符串和元組是序列。字典、集合和許多其它的可迭代對象不是序列。
我們按照指示實現一個適用於所有可迭代對象而不只是序列的循環結構。
可迭代對象是什麼?在Python的世界,一個可迭代對象是你可以使用一個for循環來進行循環的任何對象。
可迭代對象不總是可索引的,因為它們不總有長度,而且它們不總是有限的。
下面是一個無限的迭代對象,循環時它得到5的倍數:
當我們使用for循環時,我們可以循環遍歷這個迭代,像這樣:
如果我們從上面的for循環刪除break條件,它將永遠繼續列印。
因此可循環對象可以無限長:這意味著在我們遍歷一個可循環對象之前,我們不總是能把它轉換成一個列表(或者其它的序列)。我們需要以某種方式讓可迭代對象中的每一項分別迭代,同樣的方式for循環適用。
可迭代對象和迭代器好吧,我們已經定義了可迭代對象,但是可迭代對象究竟如何在Python中運作?
可以傳遞所有的可迭代對象到內置的iter函數來得到一個迭代器。
這是一個有趣的事實,但是什麼是迭代器?
迭代器只有一個工作:返回可迭代對象中的「下一個」項。迭代器有點像tally counters,但是它們沒有一個復位按鈕,而且在可迭代對象中它們給出下一個項,而不是給出下一個數字。
從任何可迭代對象可以得到一個迭代器:
並且可以傳遞迭代器到next函數來得到它們的下一項:
因此可以傳遞迭代器到內置的next函數來得到下一項,並且如果沒有下一項(因為已經到達結尾)將引發一個StopIteration異常。
因此在可迭代對象上調用iter得到迭代器。而且在迭代器上調用next得到下一項或者如果沒有更多項引發一個StopIteration異常。
實際上不止這些。可以傳遞迭代器到內置的iter函數來返回它們自身。這意味著迭代器也是可迭代對象。
這一事實導致了一些有趣的後果,而那我們目前沒有時間深入。我們將保存這個討論作為未來的學習探索……
迭代協議是一個很有意思的術語,它的意思是「可迭代對象在Python中究竟如何運作」。
讓我們從Python的角度重新定義可迭代對象。
可迭代對象:
1. 可以被傳遞到iter函數來得到可迭代對象。
2. 沒有第2個。需要一個可迭代對象。
迭代器:
1. 可以被傳遞到next函數,而這得到它們的下一項或者引發StopIteration
2. 當被傳遞到iter函數,返回它們自身。
這些話反過來也成立。意思是:
1. 傳遞到iter沒有任何錯誤的任何東西是一個可迭代對象。
2. 傳遞到next沒有任何錯誤(除了StopIteration)的任何東西是一個迭代器。
3. 傳遞到iter返回返回自身的任何東西是一個迭代器。
使用迭代器循環根據我們學到的關於可迭代對象和迭代器的知識,現在應該可以不使用for循環重建一個for循環。這個while循環手動循環某些iterable,列印每個項:
我們可以使用任何可迭代對象調用這個函數,並且它將循環:
上述函數基本上與使用一個for循環的函數相同:
這個for循環自動地做我們手動做的工作:調用iter來得到一個可迭代對象,然後重複調用next直到引發StopIteration異常。
迭代協議用於for循環、元組拆封和所有適用通用可迭代對象的內置函數。使用迭代協議(手動或自動)是唯一的在Python中任何可迭代對象循環的通用方法。
For循環:比它們看起來更複雜現在我們準備完成面試官分配的非常愚蠢的任務。我們將通過手動使用iter和next循環可迭代對象,從代碼中刪除所有的for循環。在研究這個任務中,我們學到了什麼?
所有你可以循環的是一個可迭代對象。循環可迭代對象通過從一個可迭代對象得到迭代器,然後重複請求迭代器得到下一個項來工作。
迭代器和可迭代對象工作的方式稱為迭代協議。列表生成式、元組拆封、for循環和所有其它的迭代協議中迭代依賴的形式。
我會在以後的文章中更多的探討迭代器。目前知道迭代器是Python中所有迭代的幕後。
英文原文:http://treyhunner.com/2016/12/python-iterator-protocol-how-for-loops-work/