天天看點

python疊代器和生成器

到目前為止,您可能已經注意到大多數容器對象都可以使用 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 是屬性但不是全局名稱。 顯然,使用這個将違反命名空間實作的抽象,應當僅被用于事後調試器之類的場合。