Python - Generator

Share on:

Generator

本文要來說明 generator 這東西, 但在了解這個東西之前, 最好先對 iterableiterator 有所了解, 會容易理解它.

名詞定義 (自己消化吸收出來的)

  • Iterable: 可迭代物件(介面), 它實作了 __iter__(), 它可回傳 Iterator.
  • Iterator: 迭代器物件. 它實作了上述的 Iterable 介面, 並且額外實作了 __next__(). 它的作用是:
    • 將指標指向下一個, 讓下次可以繼續調用.
    • 返回當前結果.
  • Generator: 一種比較特殊的 Iterator(繼承), 但內部實作了 yieldsend()
 1# Iterable 與 Iterator
 2from collections import Iterable, Iterator
 3a = [1, 2, 3]
 4b = iter(a)  # <list_iterator object at 0x10b705890>
 5
 6print(isinstance(a, Iterator))  # False
 7print(isinstance(b, Iterator))  # True
 8print(isinstance(a, Iterable))  # True
 9print(isinstance(b, Iterable))  # True
10
11# 因為 a 是個 iterable, 所以透過 iter(a) 取得它的 Iterator
12# 它可以透過 next(b) 一個一個慢慢取出來 (b 內部 container 儲存得值隱含著被 pop 掉了)
13# 也可透過 list(b) 一口氣取出來 (b 內部 container 瞬間被抽乾)
14c = list(b)     # [1, 2, 3]
15d = list(b)     # []
16
17b == None       # False
18
19e = iter(a)     # <list_iterator object at 0x10b705850>
20next(e)  # 1
21next(e)  # 2
22next(e)  # 3
23next(e)  # Exception StopIteration

for a in b 的原理, 其實就是先調用 iter(b), 取得 Iterator. 如此便可使用 next 方法, 一個一個叫出來處理, 直到 StopIteration.

該死的 Generator (產生器, 生成器)

generator: A function which returns a generator iterator. It looks like a normal function except that it contains yield expressions for producing a series of values usable in for-loop or that can be retrieved at a time with the next() function.

generator iterator: An object created by a generator function.

generator expression: An expression that returns an iterator.

自己理解的白話文:

  • generator 就是個會 yield generator iterator 的 function.
  • generator iterator: 被 generator function 回傳的東西
  • 如果一個 function 裡面有 yield, 那麼這個 function 就稱為 generator, 它會透過 yield 回傳 generator.
 1# Generator
 2a = (elem for elem in [1, 2, 3])  # <generator object <genexpr> at 0x10b842950>
 3
 4def fib():
 5  a, b = 0, 1
 6  while True:
 7    yield b
 8    a, b = b, a + b
 9
10g = fib()  # <generator object fib at 0x10b842850>

Python's generators provide a convenient way to implement the iterator protocol.

因為 generator 就是個 iterator (得實作 __iter__()__next()__). 但是 generator 只需要使用 yield, 它幫忙實現了 generator 的 __next()__.

 1# Generator 與 Iterator
 2def g():
 3  print('第1次')
 4  yield 1
 5  print('第2次')
 6  yield 2
 7  print('第3次')
 8  yield 3
 9
10f = g()  # <generator object g at 0x10b842850>
11next(f)  # '第1次'  收到 1
12next(f)  # '第2次'  收到 2
13next(f)  # '第3次'  收到 3
14next(f)  # StopIteration

generator 在遇到了 yield 就將程式的執行流程返回給了呼叫端 & 返回當前的值. 下次進入的時候, 會接在上次中斷的地方繼續... 這樣的概念就如同 generator 的 將指標指向下一個, 方便下次迭代 & 返回當前的值

generator 的 yield 可理解成 中斷服務子程序的斷點. 爾後每次對 generator 調用 next() 時, 就會回到斷點之後繼續執行

References

comments powered by Disqus