iT邦幫忙

1

[Python]B11─產⽣器表示式(generator)

Hi! 大家好,我是Eric,這次教大家Python的產生器表示式(generator)!
/images/emoticon/emoticon07.gif

■ 產生器表示式(generator)

■ 產⽣器表示式

  • 列表推導使用方括號,而產生器表示式使用圓括號
[n ** 2 for n in range(12)]  #典型的列表推導
(n ** 2 for n in range(12))  #典型的產生器表示式
  • 注意:直接輸出產生器表達式並不會列印出內容。要列印出產生器表達式產生的全部內容
    的⼀個方法是通過向 list 產生器傳遞參數
list[(n ** 2 for n in range(12))]
  • 列表是值的集合,而產生器是產生值的方法
    當你建立⼀個列表的時候,你實際上在產生⼀個數值的集合,並且需要花費⼀定的記憶體開銷。
    當你建立⼀個產生器的時候,你並沒有建立⼀個數值的集合,而是建立了⼀個產生這些數值的方法。
    這兩者都具有相同的迭代器介面:
L = [n ** 2 for n in range(12)]      #列表推導
for val in L:
      print(val, end=' ')
          
G = (n ** 2 for n in range(12))     #產生器表示式
for val in G:
      print(val, end=' ')
      
# 區別是:產生器表達式並不會在數值被需要之前計算出它們。這並不僅僅可以提高記憶體效率,
  也能提高計算效率!這也意味著⼀個列表的大小受到可用記憶體的限制,而產生器表達式的大小是無限的!
  • 我們可以透過itertools中定義的count迭代器建立一個無限的產生器表示式
itertools import count
count()
      
for i in count():
      print(i, end=' ')
      if i >= 10: break
  • 另一個例子
factors = [2, 3, 5, 7]
G = (i for i in count() if all(i % n > 0 for n in factors))
for val in G:
      print(val, end=' ')
       if val > 40: break
  • 列表可以被迭代多次,產生器表示式是一次性的
    我們可以直接對⼀個列表進行如下操作
L = [n ** 2 for n in range(12)]
for val in L:
     print(val, end=' ')
print()
      
for val in L:
     print(val, end=' ')
     
#另⼀方面,產⽣器表示式在⼀次迭代後就被用盡
G = (n ** 2 for n in range(12))
list(G)
list(G)

#這個特性可以變得非常實用,因為這意味著迭代可以被停止和繼續
G = (n**2 for n in range(12))
for n in G:
      print(n, end=' ')
      if n > 30: break
          
print("\ndoing something in between")
      
for n in G:
       print(n, end=' ')
       
#這個特性在對硬碟上的資料檔案集合進行處理時非常實用,意味著我們可以非常輕鬆地批量處理資料,
 讓產生器負責追蹤那些你尚未處理的區塊。

■ 產⽣器函式:關鍵字yield

  • 在之前的章節中我們看到了列表推導是建立相對簡單的列表的最佳途徑,而常規的 for 循環在更加複雜的情況下更為 合適。對於產生器表達式來說這也是成立的,我們可以通過產生器函式yield 語句來建立更加複雜的產生器。
    我們有兩種方式組成同一個列表:
L1 = [n ** 2 for n in range(12)]
    
L2 = []
for n in range(12):
    L2.append(n ** 2)
        
print(L1)
print(L2)

#同樣地,我們也有兩種方式組等價的產生器:
G1 = (n ** 2 for n in range(12))

def gen():
    for n in range(12):
        yield n ** 2
            
G2 = gen()
print(*G1)
print(*G2)
  • ⼀個產生器函式是這樣的:它不使用 return 回傳⼀個值(僅回傳⼀次),而是使用 yield 來產生⼀個包含(可能 是無窮多的)數值的序列。
    和產生器表達式中⼀樣,在部分迭代(兩次迭代的中間,此時產生器並沒有被從頭到尾執行完,故稱部分迭代)的間隔 中產生器的狀態得到保留。
    但是如果我們需要產生器的⼀個全新的副本,我們只需要簡單地重新呼叫那個函式即可。

■ case:質數產生器

  • 產生⼀個包含候選數值的列表
L = [n for n in range(2, 40)]
print(L)
  • 對於候選數值序列的第⼀個值,移除它的倍數
L = [n for n in L if n == L[0] or n % L[0] > 0]
print(L)
  • 對於候選數值序列的第二個值,移除它的倍數
L = [n for n in L if n == L[1] or n % L[1] > 0]
print(L)
  • 對於候選數值序列的第三個值,移除它的倍數
L = [n for n in L if n == L[2] or n % L[2] > 0]
print(L)
  • 如果我們不斷在⼀個足夠大的列表上重複這個過程足夠多次,我們就能產生足夠多我們需要的質數。
    讓我們把計算的邏輯封裝成為⼀個產生器表示式:
def gen_primes(N):
     """Generate primes up to N"""
     primes = set()
     for n in range(2, N):
         if all(n % p > 0 for p in primes):
            primes.add(n)
            yield n
print(*gen_primes(100))

■ Refer to《Python 旋風之旅,[正體中文]Will保哥》的第13章


1 則留言

0
Homura
iT邦高手 2 級 ‧ 2019-03-21 15:52:52

文章怎麼一堆奇怪的符號啊?
https://ithelp.ithome.com.tw/upload/images/20190321/20109839LZJcycAQ7c.png

Homura您好,感謝提醒,有些字的字型不符合,已經修正囉!若有問題歡迎再跟我說,感謝您的熱心

我要留言

立即登入留言