迭代器生成器閱讀【Python3.8官網文檔】


1、迭代器

for element in [1, 2, 3]:
    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='')

在幕后,for 語句會在容器對象(即列表、元組等)上調用 iter()

iter() 返回一個定義了 __next__() 方法的迭代器對象,__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

給類添加迭代器行為:定義一個 __iter__() 方法來返回一個帶有 __next__() 方法的對象。 如果類已定義了 __next__(),則 __iter__() 可以簡單地返回 self:

class Reverse:
    """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

2、生成器

2.1、yield 表達式

yield_atom       ::=  "(" yield_expression ")"
yield_expression ::=  "yield" [expression_list | "from" expression]

只能在函數定義的內部使用 yield 表達式,在一個函數體內使用 yield 表達式會使這個函數變成一個生成器函數

def gen():  # defines a generator function
    yield 123

2.2、簡要理解概念

官網原文

generator--生成器

返回一個 generator iterator 的函數。

它看起來很像普通函數,不同點在於其包含 yield 表達式,以便產生一系列值供給 for 循環使用或是通過 next() 函數逐一獲取。

通常是指生成器函數,但在某些情況下也可能是指生成器迭代器。如果需要清楚表達具體含義,請使用全稱以避免歧義。

generator iterator--生成器迭代器

generator 函數所創建的對象。

每個 yield 會臨時暫停處理,記住當前位置執行狀態(包括局部變量和掛起的 try 語句)。

當該生成器迭代器恢復時,它會從離開位置繼續執行(這與每次調用都從新開始的普通函數差別很大)。

generator expression--成器表達式

返回一個迭代器的表達式。

它看起來很像普通表達式后面帶有定義了一個循環變量、范圍的 for 子句,以及一個可選的 if 子句。

以下復合表達式會為外層函數生成一系列值:

>>> sum(i*i for i in range(10))         # sum of squares 0, 1, 4, ... 81
285

2.3、生成器-迭代器的方法

被用於控制生成器函數的執行。

請注意:生成器已經在執行時,調用以下任何方法都會引發 ValueError 異常。

  • generator.next()

    開始一個生成器函數的執行,或從上次執行的 yield 表達式位置恢復執行

    當一個生成器函數通過 __next__() 方法恢復執行時,當前的 yield 表達式總是取值為 None。【注:注意區分 yield 表達式的值和返回給調用者的值】

    隨后會繼續執行到下一個 yield 表達式,其 expression_list 的值會返回給 __next__() 的調用者。

    如果生成器沒有產生下一個值就退出,則將引發 StopIteration 異常。

    此方法通常是隱式地調用,例如通過 for 循環或是內置的 next() 函數

  • generator.send(value)

    恢復執行,並向生成器函數“發送”一個值。 value 參數將成為當前 yield 表達式的結果

    send() 方法會返回生成器所產生的下一個值,或者如果生成器沒有產生下一個值就退出則會引發 StopIteration。

    當調用 send() 來啟動生成器時,它必須以 None 作為調用參數,因為這時沒有可以接收值的 yield 表達式

  • generator.throw(type[, value[, traceback]])

    在生成器暫停的位置引發 type 類型的異常,並返回該生成器函數所產生的下一個值。

    如果生成器沒有產生下一個值就退出,則將引發 StopIteration 異常。

    如果生成器函數沒有捕獲傳入的異常,或引發了另一個異常,則該異常會被傳播給調用者。

  • generator.close()

    在生成器函數暫停的位置引發 GeneratorExit。

    如果之后生成器函數正常退出、關閉或引發 GeneratorExit (由於未捕獲該異常)則關閉並返回其調用者。 如果生成器產生了一個值,關閉會引發 RuntimeError。

    如果生成器引發任何其他異常,它會被傳播給調用者。 如果生成器已經由於異常或正常退出則 close() 不會做任何事。

2.4、生成器理解

# 理解生成器的工作過程
>>> def reverse(data):
...     for index in range(len(data)-1, -1, -1):
...         yield data[index]
...

>>> for char in reverse('golf'):
...     print(char)
...
f
l
o
g

# next(iterator[, default]) : Return the next item from the iterator.
# 上面的for循環相當於:
>>> g = reverse('golf')
>>> next(g)
'f'
>>> next(g)
'l'
>>> next(g)
'o'
>>> next(g)
'g'

# 或相當於:
>>> g.__next__()
'f'
>>> g.__next__()
'l'
>>> g.__next__()
'o'
>>> g.__next__()
'g'
>>> g.__next__()   # 迭代完了
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

當一個生成器函數被調用的時候,它返回一個迭代器,稱為生成器迭代器。

然后這個生成器迭代器來控制生成器函數的執行。當這個生成器迭代器的某一個方法被調用的時候【注:如__next__()】,生成器函數開始執行。

這時會一直執行到第一個 yield 表達式,在此執行再次被掛起,給生成器迭代器的調用者返回 expression_list 的值。【注:也就是返回'f''i'等值。參考 yield 表達式的語法格式部分】

掛起后,所有局部狀態都被保留下來,包括局部變量的當前綁定,指令指針,內部求值棧和任何異常處理的狀態。

通過調用生成器迭代器的某一個方法,生成器函數繼續執行,此時函數的運行就和 yield 表達式只是一個外部函數調用的情況 完全一致。【注:繼續執行時直接調用 yield 表達式】

重新開始后,yield 表達式的值取決於調用的哪個方法來恢復執行。 如果用的是 __next__() (通常通過語言內置的 for 或是 next() 來調用) 那么結果就是 None。否則,如果用 send(), 那么結果就是傳遞給 send 方法的值。
【注:是“yield 表達式的值”;參考下一節的generator.__next__()方法理解】

# 理解:重新開始后,yield 表達式的值取決於調用的哪個方法來恢復執行。
>>> def reverse(data):
...     for index in range(len(data)-1, -1, -1):
...         y = yield data[index]
...         print(y)
...
>>> g = reverse('golf')
>>> g
<generator object reverse at 0x000001EE2B4875E8>
>>> g.__next__()
'f'
>>> g.__next__()
None
'l'
...

>>> g = reverse('golf')
>>> g.send(None)
'f'
>>> g.send('aa')
aa
'l'
...

2.5、例子

# 整體理解生成器
>>> def echo(value=None):
...     print("Execution starts when 'next()' is called for the first time.")
...     try:
...         while True:
...             try:
...                 value = (yield value)   
...             except Exception as e:
...                 value = e
...     finally:
...         print("Don't forget to clean up when 'close()' is called.")
...
>>> generator = echo(1)
>>> print(next(generator))
Execution starts when 'next()' is called for the first time.
1
>>> print(next(generator))  # 迭代器里沒有值了
None

# 恢復執行,並向生成器函數“發送”一個值。 value 參數將成為當前 yield 表達式的結果
>>> print(generator.send(2))  # 這里的2發送給了`value = (yield value)`里的左邊的value
2
>>> generator.throw(TypeError, "spam")
TypeError('spam',)
>>> generator.close()
Don't forget to clean up when 'close()' is called.

3、生成器表達式

生成器表達式是用圓括號括起來的緊湊形式生成器標注。

generator_expression ::=  "(" expression comp_for ")"

生成器表達式會產生一個新的生成器對象。其句法與推導式相同,區別在於它是用圓括號而不是用方括號或花括號括起來的

在生成器表達式中使用的變量會在為生成器對象調用 __next__() 方法的時候以惰性方式被求值(即與普通生成器相同的方式)。

但是,最左側 for 子句內的可迭代對象是會被立即求值的,因此它所造成的錯誤會在生成器表達式被定義時被檢測到,而不是在獲取第一個值時才出錯。

后續的 for 子句以及最左側 for 子句內的任何篩選條件無法在外層作用域內被求值,因為它們可能會依賴於從最左側可迭代對象獲取的值。

例如:

(x*y for x in range(10) for y in range(x, x+10)).

圓括號在只附帶一個參數的調用中可以被省略。 詳情參見 調用 一節。

為了避免干擾到生成器表達式本身的預期操作,禁止在隱式定義的生成器中使用 yield 和 yield from 表達式


英文官方文檔

中文官方文檔

幫助理解


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM