Python—生成器

  • 2020 年 1 月 13 日
  • 筆記

# 生成器  # 通過列表生成式,我們可以直接創建一個列表  # 但是,受到記憶體限制,列表容量肯定是有限的  # 創建一個100萬個元素的列表,不僅佔用很大的存儲空間,如果我們僅僅需要訪問前面幾個元素,那後面絕大多數元素佔用的空間都白白浪費了  # 如果列表元素可以按照某種演算法推算出來,那我們可以在循環的過程中不斷推算出後續的元素,這樣就不必創建完整的list,從而節省大量的空間  # Python中,這種一邊循環一邊計算的機制,稱為生成器:generator    from collections import Iterable    # 創建一個list  L = [x * x for x in range(10)]  print('L:', L)    # 創建一個generator  g = (x * x for x in range(10))  print('g:', g)    # 創建L和g的區別僅在於最外層的[]和(),L是一個list,而g是一個generator    # 使用for列印generator的每一個元素,因為generator是可迭代對象  print('查看g是否可迭代:', isinstance(g, Iterable))  for n in g:      print(n)    # 用函數列印斐波那契數列(用列表生成式寫不出來)      def fib(max):      l = []      n, a, b = 0, 0, 1      while n < max:          l.append(b)          a, b = b, a + b          n = n + 1      return l    print('fib(10):', fib(10))      # 把fib函數變成generator,只需把print(b)改為yield b就可以了      def fib(max):      n, a, b = 0, 0, 1      while n < max:          yield b          a, b = b, a + b          n = n + 1    print('fib(20) for generator:', fib(10))  fibList = []  for e in fib(10):      fibList.append(e)  print('fibList:', fibList)    # 如果一個函數定義中包含yield關鍵字,那麼這個函數就不再是一個普通函數,而是一個generator  # generator和函數的執行順序不一樣  # 函數是順序執行,遇到return語句或者最後一行就返回  # 而變成generator的函數,在每次調用next()的時候執行,遇到yield語句返回,再次執行時從上次返回的yield語句處繼續執行    # 定義一個generator,依次返回數字1,3,5:      def odd():      print('step 1')      yield 1      print('step 2')      yield 3      print('step 3')      yield 5      # 在調用generator時,首先要生成一個generator對象,然後用next()函數不斷獲得下一個返回值  o = odd()  print(next(o))  print(next(o))  print(next(o))  # StopIteration  # print(next(o))    # 可以看到,odd不是普通函數,而是generator,在執行過程中,遇到yield就中斷,下次又繼續執行  # 執行3次yield後,已經沒有yield可以執行了,所以,第4次調用next(o)就會報錯  # 回到fib的例子,我們在循環過程中不斷調用yield,就會不斷中斷。當然要給循環設置一個條件來退出循環,不然就會產生一個無限數列出來  # 同樣的,把函數改成generator後,我們基本上從來不會用next來獲取下一個返回值,而是直接使用for循環來迭代