為什麼要使用 Python 生成器?該如何使用 Python 生成器?
自從 PEP 255引入生成器以來,它就是Python中重要的一部分.
生成器允許你定義一個有迭代器行為的函數.
它允許程序猿更快,更簡單並且以一個乾淨的方式創建一個迭代器.
那麼什麼是迭代器呢, 你或許會問?
iterator 迭代器是一個可以被迭代的(循環)對象. 它可以抽象為一個裝著數據同時有著可迭代對象的行為的容器.或許你已經每天在使用一些可迭代的對象: 諸如字元串,列表,字典或其它名字的對象.
一個迭代器是一個實現了迭代器介面 Iterator Protocol的類. 這個介面為類提供了兩個方法: __iter__ 和 __next__.
嗯~ 回到上一步. 你為什麼想要創建一個迭代器呢?
節省內存空間
當實例化後,迭代器並不會計算它每一個項的值,他們只會等你訪問這些項的時候採取計算。這也就是眾所周知的惰性求值。
當你有一個非常大的數據集需要計算時,惰性求值是很有用處的。它允許你馬上就能開始使用數據,儘管整個數據集還在計算中。
假設我們想要獲得小於某個最大值的所有素數。
我們先定義一個函數,它可以檢查一個數字是否為素數:
def check_prime(number): for divisor in range(2, int(number ** 0.5) + 1): if number % divisor == 0: return False return True
然後,我們定義一個迭代器類,包含__iter__ 和 __next__ 方法。
class Primes: def __init__(self, max): self.max = max self.number = 1 def __iter__(self): return self def __next__(self): self.number += 1 if self.number >= self.max: raise StopIteration elif check_prime(self.number): return self.number else: return self.__next__()
Primes 類通過給定一個最大值來實例化。如果下一個素數比最大值max還要大,迭代器就會拋出一個StopIteration異常來把迭代器停掉。
當我們請求迭代器中的下一個元素時,它會給number加1並檢查這個數字是否為素數。如果不是,它會再次調用__next__直到number成為素數。一旦如此,迭代器就將這個數字返回。
通過使用迭代器,我們並不會在內存中創建一個包含很多素數的列表。相反,我們將會在每次請求下一個素數時才去生成它。
讓我們來試一試:
primes = Primes(100000000000)print(primes)for x in primes: print(x) ......<__main__.Primes object at 0x1021834a8>235711...
對 Primes 對象的每一次迭代都調用了 __next__ 來生成下一個素數。
迭代器只可以被迭代一輪。如果你嘗試再迭代primes一輪,它將不會返回任何值,表現得就像個空列表。
既然我們已經知道了什麼是迭代器,以及怎麼製作一個迭代器,我們接下來將繼續來看看生成器。
生成器
回想下,生成器函數允許我們以一種更簡單的方式來創建迭代器。
生成器給Python引入了yield聲明。它用起來有點像return,因為它會返回一個值。
區別在於yield會保存函數的狀態。在函數下一次被調用時,將會從其離開的地方繼續執行,並且變數值也與它之前執行yield操作前相同。
如果把我們的Primes迭代器轉換為生成器,它看起來會像這樣:
def Primes(max): number = 1 while number < max: number += 1 if check_prime(number): yield numberprimes = Primes(100000000000)print(primes)for x in primes: print(x)......<generator object Primes at 0x10214de08>235711...
現在真是太pythonic了!我們還能再給力點嗎?
當然!我們可以使用 PEP 289中介紹的生成器表達式。
這相當於是生成器的列表推導式。它用起來與列表推導式相同,不過表達式由()包裹而不是[]。
下面的表達式可以代替我們上面的生成器函數:
primes = (i for i in range(2, 100000000000) if check_prime(i))print(primes)for x in primes: print(x)......<generator object <genexpr> at 0x101868e08>235711...
這就是Python生成器的美妙之處。
總結...
- 生成器允許你以一種非常pythonic的方式來創建迭代器。
- 迭代器允許惰性求值,只有在請求下一個元素時迭代器對象才會去生成它。這對於非常大的數據集是很有用的。
- 迭代器和生成器都只能被迭代一輪。
- 生成器函數比迭代器更好。
- 生成器表達式比迭代器更好(只在簡單情況下如此)。
你也可以來看看我的這篇文章 explanation ,看看我是怎樣使用Python在Medium上找到有趣的人並關注他們的。
原 文:How?—?and why?—?you should use Python Generators
譯 文:PythonCaff 作 者:Summer
更多文章:SDK.CN - 中國領先的開發者服務平台
推薦閱讀:
※Python工程師面試必備25條Python知識點
※有趣的演算法謎題之2016
※為什麼我安裝的matlab 一直提醒激活,激活成功後還顯示激活?
※python及numpy,pandas易混淆的點
※Python庫Numpy里ndarray.ndim 是什麼意思?
TAG:Python |