到目前為止,您可能已經注意到大多數容器對象都可以使用 for 語句:
print(element) for element in (1, 2, 3): print(element) for key in {'one':1, 'two':2}: print(key) for char in "123": print(char) for line in open("myfile.txt"): print(line, end='')
這種訪問風格清晰、簡潔又方便。 迭代器的使用非常普遍並使得 Python 成為一個統一的整體。 在幕后,for 語句會調用容器對象中的 iter()。 該函數返回一個定義了 __next__() 方法的迭代器對象,該方法將逐一訪問容器中的元素。 當元素用盡時,__next__() 將引發 StopIteration 異常來通知終止 for 循環。 你可以使用 next() 內置函數來調用 __next__() 方法;這個例子顯示了它的運作方式:
>>> s = 'abc' >>> it = iter(s) >>> it <iterator object at 0x00A1DB50> >>> next(it) 'a' >>> next(it) 'b' >>> next(it) 'c' >>> next(it) Traceback (most recent call last): File "<stdin>", line 1, in <module> next(it) StopIteration
看過python迭代器協議的幕后機制,給你的類添加迭代器行為就很容易了。 定義一個 __iter__() 方法來返回一個帶有 __next__() 方法的對象。 如果類已定義了 __next__(),則 __iter__() 可以簡單地返回 self:
"""Iterator for looping over a sequence backwards.""" def __init__(self, data): self.data = data self.index = len(data) def __iter__(self): return self def __next__(self): if self.index == 0: raise StopIteration self.index = self.index - 1 return self.data[self.index] >>> >>> rev = Reverse('spam') >>> iter(rev) <__main__.Reverse object at 0x00A1DB50> >>> for char in rev: ... print(char) ... m a p s
python生成器
Generator 是一個用於創建迭代器的簡單而強大的工具。 它們的寫法類似標准的函數,但當它們要返回數據時會使用 yield 語句。 每次對生成器調用 next() 時,它會從上次離開位置恢復執行(它會記住上次執行語句時的所有數據值)。 顯示如何非常容易地創建生成器的示例如下:
for index in range(len(data)-1, -1, -1): yield data[index] >>> >>> for char in reverse('golf'): ... print(char) ... f l o g
可以用生成器來完成的操作同樣可以用前一節所描述的基於類的迭代器來完成。 但生成器的寫法更為緊湊,因為它會自動創建 __iter__() 和 __next__() 方法。
另一個關鍵特性在於局部變量和執行狀態會在每次調用之間自動保存。 這使得該函數相比使用 self.index 和 self.data 這種實例變量的方式更易編寫且更為清晰。
除了會自動創建方法和保存程序狀態,當生成器終結時,它們還會自動引發 StopIteration。 這些特性結合在一起,使得創建迭代器能與編寫常規函數一樣容易。
生成器表達式
某些簡單的生成器可以寫成簡潔的表達式代碼,所用語法類似列表推導式,將外層為圓括號而非方括號。 這種表達式被設計用於生成器將立即被外層函數所使用的情況。 生成器表達式相比完整的生成器更緊湊但較不靈活,相比等效的列表推導式則更為節省內存。
例如:
>>> sum(i*i for i in range(10)) # sum of squares 285 >>> xvec = [10, 20, 30] >>> yvec = [7, 5, 3] >>> sum(x*y for x,y in zip(xvec, yvec)) # dot product 260 >>> from math import pi, sin >>> sine_table = {x: sin(x*pi/180) for x in range(0, 91)} >>> unique_words = set(word for line in page for word in line.split()) >>> valedictorian = max((student.gpa, student.name) for student in graduates) >>> data = 'golf' >>> list(data[i] for i in range(len(data)-1, -1, -1)) ['f', 'l', 'o', 'g']
腳注
[1] 存在一個例外。 模塊對象有一個秘密的只讀屬性 dict__,它返回用於實現模塊命名空間的字典;__dict 是屬性但不是全局名稱。 顯然,使用這個將違反命名空間實現的抽象,應當僅被用於事后調試器之類的場合。