生成器、迭代器作爲python的兩個高級特性,相信大家肯定耳熟能詳,都能說道上一陣,但很多時候都是說說而已,知道有這麼個東西,而且是好東西,但再看看寫過的代碼,有多少確實使用它的?
一個語音特性,在潛意識中沒用被激活,更多時候還是因爲不知道它的應用場景,這裏就從三個方面說說生成器什麼時候應該用,而且必須用。
是否需要返回列表中的所有元素?
不需要
當做出不需要的回答時,就應該選擇生成器,而不是列表,因爲生成器的主要特性就是'lazy evaluation'
生成器,只有在真正需要時才生成結果,因此在不需要列表中的所有元素,自然是沒必要去創建它們的,創建後還不用,那就是浪費資源,而且影響效率
例如,我們碰到如下場景,公司年會搞一個抽獎活動,規則是每人拿一個號,抽獎時根據這些號碼搖號,搖出的號和你對應的號一致,那就表示中獎。
下面給出一個簡單實現:
import time import random def gen_winning_numbers(): random.seed() elements = [] for i in range (0,10): time.sleep(1) # 模擬很牛逼的搖號算法但有些費時 elements.append(random.randint(1,10)) return elements random.seed() my_number = random.randint(1,10) print ("my number is " + str(my_number)) for winning_number in gen_winning_numbers(): print(winning_number) if my_number == winning_number: print ("you win!") break
gen_winning_numbers函數是一個比較耗時的函數,隨機產生10箇中獎號碼,如果my_number在這10個數中,表示中獎且程序退出,從這個實現中可以看到,不管如何至少需要等10s,才知道中獎結果。
而往往只要有一箇中獎號碼和my_number一致,就表示中獎,就無需關心其他中獎號碼,也沒必要生成其他剩餘的號碼,最優情況下,只需要1s就得到中獎結果了
使用生成器就很容易解決這個問題
import time import random def gen_winning_numbers(): random.seed() for i in range(0,10): time.sleep(1) # 模擬很牛逼的搖號算法但有些耗時 yield random.randint(1,10) random.seed() my_number = random.randint(1,10) print ("my number is " + str(my_number)) for winning_number in gen_winning_numbers(): print(winning_number) if my_number == winning_number: print ("you win!") break
函數是否需要大內存?
需要
當做出需要的回答時,就應該選擇生成器,因爲生成器在需要時創建,獲取到結果時纔開始處理,完成後在請求其他項目前可從內存中刪除,釋放內存
先看看下面這段代碼
def get_elements(): elements = [] for i in range (0,10000): elements.append("x"*10240) # 返回1W個每個元素10KB的大列表 return elements characters_count = 0 my_elements=get_elements() for i in my_elements: characters_count = characters_count + len(i) print(characters_count)
這段代碼每次執行時至少需要佔用超過100M的內存,而如果使用生成器,可是另外一番景象
def get_elements(): for i in range (0,10000): yield("x"*10240) characters_count = 0 my_elements=get_elements() for i in my_elements: characters_count = characters_count + len(i) print(characters_count)
得到同樣的結果,但只需要10KB的內存
列表生成過程中是否需要通知?
需要
當做出需要的回答時,就應該選擇生成器
在一個複雜或是耗時相對較長的列表生成過程中,用戶如果不知道當前的元素過程,一味的盲目等待,那應該是很煩人的、無法接受的。
例如,下面這個過程
import time def elements(): elements = [] for i in range (0,4): # 模擬耗時操作 time.sleep(5) elements.append(i) return elements print("start") print(elements()) print("end")
每次需要得到5個結果,必須得等待20s,更糟糕的是,在等待的過程中什麼都做不了,也不知內部具體情況,哪怕給個進度提示也好
import time def elements(): elements = [] for i in range (0,4): # 模擬耗時操作 time.sleep(5) yield(i) print("start") for i in elements(): # 輸出一個簡易進度 print(".", end="", flush=True) print() print("end")
由上可知:
如果創建一個元素的函數很耗時,如果該函數對內存佔用敏感,或是不需要列表中的所有元素,那麼最佳選擇是生成器,那其他情況下,都可以使用列表,對吧?
當然沒問題,但是如果選擇生成器是否更好呢,既擁有了上述優勢,也不乏列表的熟悉味道,因爲生成器轉換成列表很簡單
1. list方法
mylist = list(my_generator())
2. 列表生成式
mylist = [elem for elem in my_generator()]