Python的迭代器和產生器

來源:互聯網
上載者:User
先說迭代器,對於string、list、dict、tuple等這類容器物件,使用for迴圈遍曆是很方便的。在後台for語句對容器物件調用iter()函數,iter()是python的內建函數。iter()會返回一個定義了next()方法的迭代器對象,它在容器中逐個訪問容器內元素,next()也是python的內建函數。在沒有後續元素時,next()會拋出一個StopIteration異常,通知for語句迴圈結束。比如:

>>> s = 'abc'>>> it = iter(s)>>> it>>> next(it)'a'>>> next(it)'b'>>> next(it)'c'>>> next(it)Traceback (most recent call last): File "", line 1, in StopIteration

上面說的都是python內建的容器物件,它們都實現了相應的迭代器方法,那如果是自訂類需要遍曆怎麼辦?方法很簡單,對這個類AClass,實現一個__iter__(self)方法,使其返回一個帶有__next__(self)方法的對象就可以了。如果你在AClass剛好也定義了__next__(self)方法(一般使用迭代器都會定義),那在__iter__裡只要返回self就可以。廢話少說,先上代碼:

class Fib(object):  def __init__(self, max):    super(Fib, self).__init__()    self.max = max  def __iter__(self):    self.a = 0    self.b = 1    return self  def __next__(self):    fib = self.a    if fib > self.max:      raise StopIteration    self.a, self.b = self.b, self.a + self.b    return fibdef main():  fib = Fib(100)  for i in fib:    print(i)if __name__ == '__main__':  main()

簡單講下代碼會幹什麼,定義了一個Fib類,用於產生fibonacci序列。用for遍曆時會逐個列印產生的fibonacci數,max是產生的fibonacci序列中數字大小的上限。

在類的實現中,定義了一個__iter__(self)方法,這個方法是在遍曆時被iter()調用,返回一個迭代器。因為在遍曆的時候,是直接調用的python內建函數iter(),由iter()通過調用__iter__(self)獲得對象的迭代器。有了迭代器,就可以逐個遍曆元素了。而逐個遍曆的時候,也是使用內建的next()函數通過調用對象的__next__(self)方法對迭代器對象進行遍曆。所以要實現__iter__(self)和__next__(self)。而且因為實現了__next__(self),所以在實現__iter__(self)的時候,直接返回self就可以。

為了更好理解,我再簡單重複下上面說的那一段:在迴圈遍曆自訂容器物件時,會使用python內建函數iter()調用遍曆對象的__iter__(self)獲得一個迭代器,之後再迴圈對這個迭代器使用next()調用迭代器對象的__next__(self)。__iter__只會被調用一次,而__next__會被調用 n 次。

下面說產生器。

產生器(Generator)是建立迭代器的簡單而強大的工具。它們寫起來就像是正規的函數,只是在需要返回資料的時候使用yield語句。每次next()被調用時,產生器會返回它脫離的位置(它記憶語句最後一次執行的位置和所有的資料值)。以下樣本示範了產生器可以很簡單的建立出來:

>>> def reverse(data):...   for index in range(len(data)-1, -1, -1):...     yield data[index]... >>> for char in reverse('hello'):...   print(char)... olleh

關於迭代器和產生器的區別,產生器能做到迭代器能做的所有事,而且因為自動建立了__iter__()和 next()方法,產生器顯得特別簡潔,而且產生器也是高效的。除了建立和儲存程式狀態的自動方法,當發生器終結時,還會自動拋出StopIteration異常。一個帶有yield的函數就是一個 產生器,它和普通函數不同,產生一個 generator 看起來像函數調用,但不會執行任何函數代碼,直到對其調用next()(在 for 迴圈中會自動調用next())才開始執行。雖然執行流程仍按函數的流程執行,但每執行到一個yield語句就會中斷,並返回一個迭代值,下次執行時從yield的下一個語句繼續執行。看起來就好像一個函數在正常執行的過程中被yield中斷了數次,每次中斷都會通過yield返回當前的迭代值(yield暫停一個函數,next()從其暫停處恢複其運行)。

另外對於產生器,python還提供了一個產生器運算式:類似與一個yield值的匿名函數。運算式本身看起來像列表推到, 但不是用方括弧而是用圓括弧包圍起來:

>>> unique_characters = {'E', 'D', 'M', 'O', 'N', 'S', 'R', 'Y'}>>> gen = (ord(c) for c in unique_characters)>>> gen at 0x7f2be4668678>>>> for i in gen:...   print(i)... 6979837782788968>>> 

如果需要,可以將產生器運算式傳給tuple、list或是set來迭代所有的值並且返回元組、列表或是集合。在這種情況下,不需要一對額外的括弧 ———— 直接將產生器運算式 ord(c) for c in unique_characters傳給tuple()等函數就可以了, Python 會推斷出它是一個產生器運算式。

最後,為什麼要使用產生器?因為效率。使用產生器運算式取代列表解析可以同時節省 cpu 和 記憶體(ram)。如果你構造一個列表的目的僅僅是傳遞給別的函數,(比如 傳遞給tuple()或者set()), 那就用產生器運算式替代吧!

以上所述就是本文的全部內容了,希望大家能夠喜歡。

  • 聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.