1. 程式人生 > >python中的yield生成器詳解

python中的yield生成器詳解

斐波那契數 html 參考 列表 ron leg www. lan 2個

#原創,轉載請先聯系

在學習生成器之前,必須先了解一下叠代器。因為生成器就是一種特殊的叠代器,而且生成器用起來更加優雅。

叠代器的詳解可以參考我的另一篇博文:https://www.cnblogs.com/chichung/p/9537969.html

先說一種比較簡單的生成器,通過例子慢慢來體會什麽是生成器。

# 列表生成式
L = [x for x in range(5)]
print(L)

#簡單的生成器
G = (x for x in range(5))  # G就是一個生成器,也是一個叠代器,叠代器也是可叠代對象,所以這個G也可以說是可叠代對象
print(next(G))
print(next(G))
print(next(G)) print(next(G)) print(next(G)) 輸出: [0, 1, 2, 3, 4] 0 1 2 3 4

把列表生成器的[]改為()就變成一個簡單的生成器。由上面的例子,我們大概可以知道,生成器就是一個叠代器,把數據一個一個拿出來,可以減少內存的負擔。

那麽,yield又是一個什麽東西呢?為什麽說他優雅呢?

當我們寫的代碼輸出的結果,想一個一個出來。有兩種常用的方法:

方法1.我們可以創建一個叠代器類,然後把代碼寫進類裏,用類來創建一個可叠代對象,然後用next()函數一個一個把結果叠代出來。

方法2.我們可以用代碼函數的合適位置加上yield,這時候這個函數就變成一個生成器了,不需要再創建一個叠代器類,不需要再寫__iter__,__next__方法了。這樣一來不是很方便,很優雅嗎?哈哈哈哈~

口說無憑,下面我們2個方法都做一下,讓你們體會一下:

我們做一個斐波那契的數列生成器。斐波那契數列的第一個數是0,第二個數是1,第三個數是第一、二個數相加,第四個數是第二、三個數相加......

方法1:

class FeiboIterator():
    def __init__(self):
        self.a = 0
        self.b = 1

    def __iter__(self):
        return self

    def __next__(self):
            num = self.a
            self.a,self.b 
= self.b,self.a+self.b return num iterator = FeiboIterator() print(next(iterator)) print(next(iterator)) print(next(iterator)) print(next(iterator)) print(next(iterator)) print(next(iterator)) print(next(iterator)) print(next(iterator)) 輸出:
0
1 2 3 5 8 13

是不是很麻煩?又要初始化,又要寫__iter__和__next__魔方方法。

方法2:

def feibo():
    a = 0
    b = 1
    while True:
        yield a  # 假如yield後面緊接著一個數據,就會把數據返回,作為next()函數或者for ...in...叠代出的下一個值
        a,b = b,a+b


generator = feibo()

print(next(generator))
print(next(generator))
print(next(generator))
print(next(generator))
print(next(generator))
print(next(generator))
print(next(generator))
print(next(generator))

輸出:
0
1
1
2
3
5
8
13

看!只有6行代碼,是不是很elegant?關於這個程序是怎麽運行的?yield是怎麽運作的?我們等下就講,現在需要註意幾點:

1.上面代碼的紅色字那裏!假如yield後面緊接著一個數據,就會把數據返回,作為next()函數或者for ...in...叠代出的下一個值。

2.假如函數中有yield,就不再是函數。而是一個能返回生成器的函數!註意!是返回,這個函數並不是一個生成器。

3.拿到函數的生成器後,可以和叠代器一樣,用next()函數獲得下一個值。

好了,該來理解一下yield是怎麽運作的了!

1.第一次喚醒生成器時,是從函數的起始位置開始,直到遇到yield,就會暫停函數,掛起函數。
2.第二次喚醒生成器時,是從yield斷點處開始,直到又遇到yield。
3.當生成器已經沒有yield,再使用next,則拋StopIteration異常。

然後,我們來理一下上面用yield寫的代碼。

第一次用next()喚醒生成器時,從函數的開頭開始運行,遇到yield a,返回a,然後暫停函數,並記住函數是運行到這個位置的。

第二次喚醒生成器,從yield a斷點處開始,然後a,b開始賦值,while True循環又遇見yield a,返回a,然後暫停函數,並記住函數是運行到這個位置的。

下面喚醒多少次都是這個道理,但是由於這個函數是死循環,所以不會沒有yield,也就不會拋出StopIteration異常。

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

其實yield還能接受值,用send方法進行傳入。代碼體會一下:

def gg():
    i = 1
    while True:
        recv = yield i
        print("接收到一個值:",recv)
        i += 1

generator = gg()

print(next(generator))
print(generator.send("456"))
print(generator.send("789"))

輸出:
1
接收到一個值: 456
2
接收到一個值: 789
3

實現過程和上面的例子一樣。

要懂得的是,yield = a,會返回a。

b = yield,會把yield接收的值賦值給b。

python中的yield生成器詳解