1. 程式人生 > >python3:生成器yield深度解析

python3:生成器yield深度解析

生成器這個章節尤其的重要,我們以後的協程的部分要用到這個知識點.
什麼是生成器函式呢?
定義為:
只要方法裡有 yield 這個關鍵字 代表不是普通的函式,就可以認為是生成器函式 .

怎麼就不普通了,咱們和普通的比較下 ,程式碼如下:

def gen_func():
    yield  1

def func():
    return  1

print(gen_func(),func())  
#列印結果 
#<generator object gen_func at 0x000000000220AE60> 1

我們可以發現 gen_func() 返回的是一個物件,並不是1.
func()返回的是1 .

有的小夥伴該說了,那我們怎麼去訪問生成器裡面的1 呢?
這個是個好問題,我們訪問物件裡的值一定要用到前面的知識,迭代器和迭代物件.
PS:看來知識都是關聯的,要想遇到什麼就學什麼,絕對沒有思路.

我也不做多解釋,用程式碼解釋來的直接 :

from collections import Iterable, Iterator


def gen_func():
    yield  1

def func():
    return  1

print(gen_func(),func())

print(isinstance(gen_func(),Iterable))  #True
print(isinstance(gen_func(),Iterator))   #True
  
print(next(gen_func()))   # 1  

借用小嶽嶽的一句話“這麼神奇呀,生成器也是迭代器”
有了這個我們就可以放心用for 迴圈進行輸出. 這太方便了吧.

那有較真的小夥伴該說了,你這不是瞎胡搞嗎,本來直接return 個返回值
直接輸出就行了,你非得再繞一大圈 然後再輸入,你感覺有意思嗎?

好像他說的也沒有錯呀,but 有個場景,我需要return 兩次甚至更多,如何做呢
按照你的方法,要更多return 唄, 好我就讓你心服口服,看看程式碼吧

def func():`在這裡插入程式碼片`
    return  1
    return  2

print(func())
#列印結果: 1

return 2 並沒有返回,為什麼呢, 因為在return 1 的時候已經斷掉了,後邊的程式碼不會執行.

小夥伴們該說了,這樣是行不通,那有什麼辦法嗎 ? 當然離不開這節的主題,答案是生成器

請看下邊的程式碼:

def gen_func():
    yield  1
    yield  2
    yield  "Andy"

print(gen_func())

for  i  in gen_func():
    print(i)

#列印結果   
#<generator object gen_func at 0x000000000288F2B0>
#1
#2
#Andy

都打印出來了, 功能強爆了.
稍微解釋下:
當我們用for 迴圈的時候,會呼叫 第一個 next() 打印出1,然後在next()打印出2
一直迴圈到報 StopIteration 異常結束.
由於我們呼叫next()會返回一個值,不呼叫不會往下返回值,這個特性很重要,
官方的叫法是惰性求值 .

為了明白惰性求值的好處, 我來舉個例子 ,

經典斐波拉契
斐波那契數列指的是這樣一個數列 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377

這個數列從第3項開始,每一項都等於前兩項之和。

用普通的方法,來 求10位之和

def func(index):
    if index<=2:
        return  1
    else:
        return func(index-1)+func(index-2)

print(func(10)) #  55

用普通的方法,也可用實現,但是我們有個問題就是 想列印它計算過程,你怎麼搞?

用普通的方法我們可以這麼寫

def func2(index):
    a=0
    b=1
    n=0
    list_num=[]
    while n<index:
        list_num.append(b)
        a,b=b,a+b
        n+=1
    return list_num

print(func2(10))
#列印結果    [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

上邊的 a,b=b,a+b 就相當於 a=b , b=a+b 這也是迴圈的遞增方式.

有個問題,我們知道用list 有個缺點就是 效能低,如果index 很大的時候,不要用print,
這是血的教訓 ,我剛剛就重啟了. 還好部落格的內容還在. 當index 是上千萬的時候,也不要呼叫方法
,非常佔記憶體,你的機器同樣也會宕機。

我們開始用 生成器的方式進行訪問如下邊

def gen_func(index):
    a=0
    b=1
    n=0

    while n<index:
        a,b=b,a+b
        n+=1
        yield b

for i in gen_func(10):
    print(i)
    #列印結果  
 1
2
3
5
8
13
21
34
55
89

我們已經實現了列印過程,為什麼它可以使用index值為成千上萬的呢,這還是離不開生成器的惰性機制,
當呼叫 yield 的時候,會把數值存在生成器裡面,直到迴圈結束.
next()的時候,呼叫一下,不呼叫就不輸出.

更多的功能我會在一節介紹,請關注