小白學 Python(21):生成器基礎

人生苦短,我選Pythonhtml

前文傳送門python

小白學 Python(1):開篇git

小白學 Python(2):基礎數據類型(上)github

小白學 Python(3):基礎數據類型(下)算法

小白學 Python(4):變量基礎操做數據結構

小白學 Python(5):基礎運算符(上)多線程

小白學 Python(6):基礎運算符(下)函數

小白學 Python(7):基礎流程控制(上)spa

小白學 Python(8):基礎流程控制(下)線程

小白學 Python(9):基礎數據結構(列表)(上)

小白學 Python(10):基礎數據結構(列表)(下)

小白學 Python(11):基礎數據結構(元組)

小白學 Python(12):基礎數據結構(字典)(上)

小白學 Python(13):基礎數據結構(字典)(下)

小白學 Python(14):基礎數據結構(集合)(上)

小白學 Python(15):基礎數據結構(集合)(下)

小白學 Python(16):基礎數據類型(函數)(上)

小白學 Python(17):基礎數據類型(函數)(下)

小白學 Python(18):基礎文件操做

小白學 Python(18):基礎文件操做

小白學 Python(19):基礎異常處理

小白學 Python(20):迭代器基礎

生成器

咱們前面聊過了爲何要使用迭代器,各位同窗應該還有印象吧(說沒有的就太過度了)。

列表太大的話會佔用過大的內存,可使用迭代器,只拿出須要使用的部分。

生成器的設計原則和迭代器是類似的,若是須要一個很是大的集合,不會將元素所有都放在這個集合中,而是將元素保存成生成器的狀態,每次迭代的時候返回一個值。

好比咱們要生成一個列表,能夠採用以下方式:

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'>

這裏使用到了關鍵字 yieldyieldreturn 很是的類似,均可以返回值,可是不一樣的是 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 上,方便你們取用。

示例代碼-Github

示例代碼-Gitee

原文出處:https://www.cnblogs.com/babycomeon/p/11854666.html

相關文章
相關標籤/搜索