Python生成器和迭代器
1、什麼是生成器
要說生成器,那就要先看列表,有了列表為啥還要生產器呢。列表可以寫成解析式,書寫簡單,執行速度快,但是列表是一次性生成全部資料,如果資料較大就需要很大的記憶體。
>>> a=[i*i for i in range(10)] >> a [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
列表a需要一次把全部資料生成出來,如果資料成千上萬就會受限制。
如果列表元素不是一次性全部生成,而是在迴圈中不斷的計算推匯出來,那就不需要一次建立列表陣列,不需要申請很大的記憶體空間,這就是生成器。
2、生成器的特點
上面的列表推導式的方括號改為圓括號,就是生成器。
>>> a=(i*i for i in range(10)) >>> a <generator object <genexpr> at 0x00000224FFB4DB48>
那麼怎麼從生成器裡取資料呢?
>>> next(a) 0 >>> next(a) 1 >>> next(a) 4 >>> next(a) 9 >>> next(a) 16 >>> next(a) 25
生成器generator有一個next方法,每次呼叫都會計算出他的下一個元素的值。生成器是可以迭代的,所以可以直接使用for語句迴圈生成器。
>>> a=[i*i for i in range(10)] >>> for i in a: print(i, end=' ') >>> 0 1 4 9 16 25 36 49 64 81
除了使用圓括號建立生成器,也可以用yeild建立生成器,含有yeild關鍵字的函式都是生成器。生成器其實是一種特殊的迭代器,但是不需要像迭代器一樣實現__iter__和__next__方法,只需要使用關鍵字yield就可以。
def gen_squares(num): for x in range(num): print("x0=",x) yield x ** 2 print("x1=",x)
呼叫生成器:
for i in gen_squares(5): print(i)
輸出如下,開始x=0,先輸出x0=0,然後yeild返回print(i)列印平方數。下次迴圈回到yeild位置執行輸出x1=1,然後迴圈x=1,輸出x0=1,再次yeild到print(i)。
x0= 0 0 x1= 0 x0= 1 1 x1= 1 x0= 2 4 x1= 2 x0= 3 9 x1= 3 x0= 4 16 x1= 4
呼叫部分做個簡單修改:
for i in gen_squares(5): print(i) if i==1: break
輸出為如下,可見yield能儲存內部狀態,並掛起中斷退出。在下一輪迭代呼叫時,從yield的地方繼續執行,並且沿用上一輪的函式內部變數的狀態,直到內部迴圈過程結束。
x0= 0 0 x1= 0 x0= 1 1
python使用生成器對延遲操作提供了支援,需要時才計算,不提前計算,這種做法節約記憶體空間,但是需要一直呼叫計算過程,可以說是時間換空間吧。
3、什麼是迭代器
實現了__iter__和__next__方法的物件都稱為迭代器。迭代器完成迭代功能,基於上一次的結果重複計算,自動記錄所在的位置,而不需要手動記錄索引,含有__iter__方法的物件都是可迭代物件。
迭代物件內建兩個方法:__next__和__iter__方法。next方法可以使迭代器自動記錄迭代位置而不需要使用者維護索引。iter方法能返回迭代器本身。
迭代器是一個有狀態的物件,在呼叫next() 的時候返回下一個值,如果容器中沒有更多元素了,則丟擲StopIteration異常。
列表、元組就實現了迭代器。
>>> a=[1,2,3,4] >>> it = a.__iter__() >>> next(it) 1 >>> next(it) 2 >>> next(it) 3 >>> next(it) 4 >>> next(it) Traceback (most recent call last): File "<pyshell#547>", line 1, in <module> next(it) StopIteration
4、實現迭代物件
class square: def __init__(self, n): self.prev = 0 self.curr = 0 self.n = n def __iter__(self): return self def __next__(self): if(self.curr < self.n): self.prv = self.curr * self.curr self.curr+=1 return self.prv else: raise StopIteration
a = square(10) for i in a: print(i) print("\r\n-------\r\n") b = square(5) for i in b: print(i)
輸出結果為:
==================== RESTART: C:/Users/plu/my_python/t1.py ==================== 0 1 4 9 16 25 36 49 64 81 ------- 0 1 4 9 16 >>>