聯(lián)合迭代器與生成器,這個內(nèi)置函數(shù)真香!
花下貓語:Python 中很多內(nèi)置函數(shù)的作用都非常大,比如說 enumerate() 和 zip(),它們使得我們在作迭代操作時極為順手。這是一篇很多年前的 PEP,提議在 Python 2.3 版本中引入 enumerate(),該文檔整合了其它幾篇 PEP 的想法(包括當時新引入的迭代器與生成器),提出了更好的實現(xiàn)方案以及函數(shù)名。經(jīng)過這么多年的發(fā)展,enumerate() 不可避免地有了一些變化,但不變的是,它跟 19 年前一樣,還是很有必要、很好用,用著真香!
PEP翻譯計劃:https://github.com/chinesehuazhou/peps-cn
摘要
本 PEP 引進了一個新的內(nèi)置函數(shù) enumerate() 來簡化常用的循環(huán)寫法。它為所有的可迭代對象賦能,作用就像字典的 iteritems() 那樣——一種緊湊、可讀、可靠的索引表示法。
基本原理
Python 2.2 在 PEP 234[3] 中提出了可迭代對象接口的概念。iter() 工廠函數(shù)作為一種通用的調(diào)用約定而被提出,深入修改了迭代器的使用方式,作為整個 Python 的統(tǒng)一規(guī)范。這種統(tǒng)一的規(guī)范就是為映射類型、序列類型和文件對象建立一個通用的可迭代對象接口。
PEP 255[1] 中提出的生成器是作為一種更容易創(chuàng)建迭代器的方法引入的,特別是具有復雜的內(nèi)部執(zhí)行過程或變量狀態(tài)的迭代器。有了生成器以后,PEP 212[2] 中關于循環(huán)的計數(shù)器的想法就有可能改進了。
那些想法是提供一種干凈的迭代語法,帶有索引和值,但不適用于所有的可迭代對象。而且,那種方法沒有生成器提供的內(nèi)存友好的優(yōu)點(生成器不會一次性計算整個序列)。
(Python貓注:關于生成器的 PEP 也有翻譯,請點擊查看 PEP-255、PEP-342、PEP-380)
新的提議是添加一個內(nèi)置函數(shù) enumerate(),在有了迭代器和生成器以后,它就可以實現(xiàn)。它為所有的可迭代對象賦能,作用就像字典的 iteritems() 那樣——一種緊湊、可讀、可靠的索引表示法。像 zip() 一樣,它有望成為一種常用的循環(huán)習語(idiom)。
(Python貓注:zip() 函數(shù)非常強,推薦閱讀《一篇文章掌握 Python 內(nèi)置 zip() 的全部內(nèi)容》)
這一提議的目的是利用現(xiàn)有的實現(xiàn),再加一點點的努力來整合。它是向后兼容的,不需要新的關鍵字。本提案將合入 Python 2.3,不需要從 __future__ 中導入。
新內(nèi)置函數(shù)的規(guī)范
def enumerate(collection):
'Generates an indexed series: (0,coll[0]), (1,coll[1]) ...'
i = 0
it = iter(collection)
while 1:
yield (i, it.next())
i += 1
注A:PEP 212 循環(huán)計數(shù)器迭代[2]討論了幾個實現(xiàn)索引的提議。有些提議只適用于列表,不像上面的函數(shù)適用于任意生成器、xrange、序列或可迭代對象。
另外,那些提議是在 Python 2.2 之前提出并評估的,但是 Python 2.2 沒有包含生成器。因此,PEP 212 中的非生成器版本有一個缺點,即會用一個巨大的元組列表,導致消耗太多內(nèi)存。
這里提供的生成器版本快速且輕便,適用于所有可迭代對象,并允許用戶在不浪費計算量的情況下中途放棄。
還有一些涉及相關問題的 PEP:整型迭代器、整型 for 循環(huán),以及一個修改 range 和 xrange 的參數(shù)的 PEP。enumerate() 提案并不排斥其它提案,即使那些提案被采納,它仍然滿足一個重要的需求——對任意可迭代對象中的元素進行計數(shù)的需求。
其它的提案給出了一種產(chǎn)生索引的方法,但沒有相應的值。如果給定的序列不支持隨機訪問,比如文件對象、生成器或用__getitem__定義的序列,這就特別成問題。
注B:幾乎所有的 PEP 審閱人都歡迎這個函數(shù),但對于“是否應該把它作為內(nèi)置函數(shù)”
存在分歧。一方提議使用獨立的模塊,主要理由是減緩語言膨脹的速度。
另一方提議使用內(nèi)置函數(shù),主要理由是該函數(shù)符合 Python 核心編程風格,適用于任何具有可迭代接口的對象。正如 zip() 解決了在多個序列上循環(huán)的問題,enumerate() 函數(shù)解決了循環(huán)計數(shù)器的問題。
如果只允許加一個內(nèi)置函數(shù),那么 enumerate() 就是最重要的通用工具,可以解決最廣泛的問題,同時提高程序的簡潔性、清晰度和可靠性。
注C:討論了多種備選名稱:
| 函數(shù)名 | 分析 |
|---|---|
| iterindexed() | 五個音節(jié)太拗口了 |
| index() | 很好的動詞,但是可能會跟 .index () 方法混淆 |
| indexed() | 很受歡迎,但是應該避免形容詞 |
| indexer() | 在 for 循環(huán)中,名詞讀起來不太好 |
| count() | 直接而明確,但常用于其它語境 |
| itercount() | 直接、明確,但被不止一個人討厭 |
| iteritems() | 與字典的 key:value 概念沖突 |
| itemize() | 讓人困惑,因為 amap.items() != list(itemize(amap)) |
| enum() | 簡練;不及enumerate 清楚;與其它語言中的枚舉太相似,但有著不同的含義 |
所有涉及“count”的名稱還有一個缺點,即隱含著計數(shù)是從 1 開始而不是從 0 開始的意思。
所有涉及“index”的名稱與數(shù)據(jù)庫語言的用法沖突,數(shù)據(jù)庫的索引表示一種排序操作,但不是線性排序。
注D:在最初的提案中,這個函數(shù)帶有可選的 start 和 stop 參數(shù)。GvR 指出,函數(shù)enumerate(seqn,4,6) 還有一種看似合理的解釋,即返回序列的第 4 和第 5 個元素的切片。為了避免歧義,這兩個可選參數(shù)被摘掉了,盡管這意味著循環(huán)計數(shù)器失去了部分的靈活性。
在從 1 開始計數(shù)的常見用例中,這種可選參數(shù)的寫法很有用,比如:
for linenum, line in enumerate(source,1): print linenum, line
(Python貓注:這篇文檔說 enumerate 沒有起止參數(shù),然而,在后續(xù)版本中(例如我用的 3.9),它支持使用一個可選的 start 參數(shù)!我暫未查到這個變更是在何時加入的,如有知情者,煩請告知我,以便修正!)
GvR 評論道:
filter 和 map 應該 die,被納入列表推導式,不增加更多的變體。我寧可引進做迭代器運算的內(nèi)置函數(shù)(例如 iterzip,我經(jīng)常舉的例子)。
我認可用某種方法并行地遍歷序列及其索引的想法。把它作為一個內(nèi)置函數(shù),沒有問題。
我不喜歡“indexed”這個名字;形容詞不是好的函數(shù)名。可以用 iterindexed() ?
Ka-Ping Yee 評論道:
我對你的提議也很滿意……新增的內(nèi)置函數(shù)(傾向于用“indexed”)是我期盼了很久的東西。
Neil Schemenauer 評論道:
新的內(nèi)置函數(shù)聽起來不錯。Guido 可能會擔心增加太多內(nèi)置對象。你最好把它們作為某個模塊的一部分。如果你用模塊的話,那么你可以添加很多有用的函數(shù)(Haskell 有很多,我們可以去“偷”)。
Magnus Lie Hetland 評論道:
我認為 indexed 會是一個有用和自然的內(nèi)置函數(shù)。我肯定會經(jīng)常使用它。我非常喜歡 indexed();+1。很高興它淘汰了 PEP-281。為迭代器添加一個單獨的模塊似乎是個好主意。
來自社區(qū)的反饋:
對于 enumerate() 提案,幾乎 100% 贊成。幾乎所有人都喜歡這個想法。
作者的注釋:
在這些評論之前,共有四種內(nèi)置函數(shù)被提出來。經(jīng)過評論之后,xmap、xfilter 和 xzip 被撤銷了。剩下的一個對 Python 來說是至關重要的。Indexed() 非常容易實現(xiàn),并且立馬就可以寫進文檔。更重要的是,它在日常編程中很有用,如果不用它,就需要顯式地使用生成器。
這個提案最初包含了另一個函數(shù) iterzip()。但之后在 itertools 模塊中實現(xiàn)成了一個 izip() 函數(shù)。
參考材料
版權
-End-
最近有一些小伙伴,讓我?guī)兔φ乙恍?nbsp;面試題 資料,于是我翻遍了收藏的 5T 資料后,匯總整理出來,可以說是程序員面試必備!所有資料都整理到網(wǎng)盤了,歡迎下載!

面試題】即可獲取
