人生苦短,我選Python
前文傳送門
生成器
我們前面聊過了為什麼要使用迭代器,各位同學應該還有印象吧(說沒有的就太過分了)。
列表太大的話會佔用過大的內存,可以使用迭代器,只拿出需要使用的部分。
生成器的設計原則和迭代器是相似的,如果需要一個非常大的集合,不會將元素全部都放在這個集合中,而是將元素保存成生成器的狀態,每次迭代的時候返回一個值。
比如我們要生成一個列表,可以採用如下方式:
list1 = [x*x for x in range(10)]
print(list1)
結果如下:
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
如果我們生成的列表非常的巨大,比如:
list2 = [x*x for x in range(1000000000000000000000000)]
結果如下:
Traceback (most recent call last):
File "D:/Development/Projects/python-learning/base-generator/Demo.py", line 3, in <module>
list2 = [x*x for x in range(1000000000000000000000000)]
File "D:/Development/Projects/python-learning/base-generator/Demo.py", line 3, in <listcomp>
list2 = [x*x for x in range(1000000000000000000000000)]
MemoryError
報錯了,報錯信息提示我們存儲異常,並且整個程序運行了相當長一段時間。友情提醒,這麼大的列表創建請慎重,如果電腦配置不夠很有可能會將電腦卡死。
如果我們使用生成器就會非常方便了,而且執行速度嗖嗖的。
generator1 = (x*x for x in range(1000000000000000000000000))
print(generator1)
print(type(generator1))
結果如下:
<generator object <genexpr> at 0x0000014383E85B48>
<class 'generator'>
那麼,我們使用了生成器以後,怎麼讀取生成器生成的數據呢?
當然是和之前的迭代器一樣的拉,使用 next()
函數:
generator2 = (x*x for x in range(3))
print(next(generator2))
print(next(generator2))
print(next(generator2))
print(next(generator2))
結果如下:
Traceback (most recent call last):
File "D:/Development/Projects/python-learning/base-generator/Demo.py", line 14, in <module>
print(next(generator2))
StopIteration
直到最後,拋出 StopIteration
異常。
但是,這種使用方法我們並不知道什麼時候會迭代結束,所以我們可以使用 for 循環來獲取每生成器生成的具體的元素,並且使用 for 循環同時也無需關心最後的 StopIteration
異常。
generator3 = (x*x for x in range(5))
for index in generator3:
print(index)
結果如下:
0
1
4
9
16
generator
非常的強大,本質上, generator
並不會取存儲我們的具體元素,它存儲是推算的算法,通過算法來推算出下一個值。
如果推算的算法比較複雜,用類似列表生成式的 for 循環無法實現的時候,還可以用函數來實現。
比如我們定義一個函數,emmmmmm,還是簡單點吧,大家領會精神:
def print_a(max):
i = 0
while i < max:
i += 1
yield i
a = print_a(10)
print(a)
print(type(a))
結果如下:
<generator object print_a at 0x00000278C6AA5CC8>
<class 'generator'>
這裏使用到了關鍵字 yield
, yield
和 return
非常的相似,都可以返回值,但是不同的是 yield
不會結束函數。
我們調用幾次這個用函數創建的生成器:
print(next(a))
print(next(a))
print(next(a))
print(next(a))
結果如下:
1
2
3
4
可以看到,當我們使用 next() 對生成器進行一次操作的時候,會返回一次循環的值,在 yield
這裏結束本次的運行。但是在下一次執行 next() 的時候,會接着上次的斷點接着運行。直到下一個 yield
,並且不停的循環往複,直到運行至生成器的最後。
還有一種與 next() 等價的方式,直接看示例代碼吧:
print(a.__next__())
print(a.__next__())
結果如下:
5
6
接下來要介紹的這個方法就更厲害了,不僅能迭代,還能給函數再傳一個值回去:
def print_b(max):
i = 0
while i < max:
i += 1
args = yield i
print('傳入參數為:' + args)
b = print_b(20)
print(next(b))
print(b.send('Python'))
結果如下:
1
傳入參數為:Python
2
上面講了這麼多,可能各位還沒想到生成器能有什麼具體的作用吧,這裏我來提一個——協程。
在介紹什麼是協程之前先介紹下什麼是多線程,就是在同一個時間內可以執行多個程序,簡單理解就是你平時可能很經常的一邊玩手機一邊聽音樂(毫無違和感)。
協程更貼切的解釋是流水線,比如某件事情必須 A 先做一步, B 再做一步,並且這兩件事情看起來要是同時進行的。
def print_c():
while True:
print('執行 A ')
yield None
def print_d():
while True:
print('執行 B ')
yield None
c = print_c()
d = print_d()
while True:
c.__next__()
d.__next__()
結果如下:
...
執行 A
執行 B
執行 A
執行 B
執行 A
執行 B
執行 A
執行 B
執行 A
執行 B
...
因為 while
條件設置的是永真,所以這個循環是不會停下來的。
這裏我們定義了兩個生成器,並且在一個循環中往複的調用這兩個生成器,這樣看起來就是兩個任務在同時執行。
最後的協程可能理解起來稍有難度,有問題可以在公眾號後台問我哦~~~
示例代碼
本系列的所有代碼小編都會放在代碼管理倉庫 Github 和 Gitee 上,方便大家取用。
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※想知道網站建置、網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計及後台網頁設計
※不管是台北網頁設計公司、台中網頁設計公司,全省皆有專員為您服務
※Google地圖已可更新顯示潭子電動車充電站設置地點!!
※帶您來看台北網站建置,台北網頁設計,各種案例分享
※別再煩惱如何寫文案,掌握八大原則!