淺談Python yield使用

零零碎碎學着python已經好久了,但是總會發現有好些知識點都沒學到,今天在牛客網看到了這樣一道題:

# 下列代碼的運行結果是什麼?
def bar(n):
    m = n
    while True:
        m+=1
        yield m
b = bar(3)
print b.next()  

題目本身沒什麼難度,所以就不多說了~

emmmm…剛看到題目的時候,果然發現了我印象裏沒有的東西——yield。迅速查了資料,大概瞭解到,一般情況下,帶有yield的函數不是一般的函數,而是一個生成器(generator)。
yield這個關鍵字的作用比較類似於return和print,均有輸出/返回結果之意,next()表示依次返回迭代結果。

下面,暫且不說generator,先以一個簡單的例子——裴波那契數列來說一說yield。

實例1:簡單輸出裴波那契數列的前n個數。

def fibo(max):
    n, a, b = 0, 0, 1
    while n < max:
         print b
         a, b = b, a + b
         n = n + 1

這樣的寫法屬於常規操作,但是,函數的可複用性不高。因爲fibo()函數沒有返回值,其他函數無法獲得該函數生成的數列。
這時我們會想到,爲了提高函數的可複用性,我們不直接打印出數列,而是去返回一個List.
從而也就得到了下面這種做法。

實例2

def fibo(max):
    n, a, b = 0, 0, 1
    L = []
    while n < max:
         L.append(b)
         a, b = b, a + b
         n = n + 1
    return L

通過以上的改動,函數的可複用性大大提高,但是,該函數在運行中佔用的內存會隨着參數 max 的增大而增大,因此如果要控制內存佔用,最好不要用 List來保存中間結果,而是通過 iterable 對象來迭代。

for i in range(1000):
    pass
# 會生成一個有1000個元素的List
for i in xrange(1000):
    pass
# 不會生成一個 1000 個元素的 List,而是在每次迭代中返回下一個數值,內存空間佔用很小。因爲 xrange 不返回 List,而是返回一個 iterable 對象。 

利用 iterable 我們可以把 fibo() 函數改寫爲一個支持 iterable 的 class,這樣也就有了下面的實例:

實例3

class Fibo(object):
    def __init__(self, max):
        self.max = max
        self.n, self.a, self.b = 0, 0, 1

    def __iter__(self):
        return self

    # 下一個迭代對象
    def next(self):
        if self.n < self.max:
            r = self.b
            self.a, self.b = self.b, self.a + self.b
            self.n += 1
            return r
        raise StopIteration()


for n in Fibo(5):
    print n

Fibo 類通過 next() 函數不斷返回數列的下一個數,內存佔用始終爲常數、

然而,

使用 class 改寫的這個版本,代碼遠遠沒有實例1簡潔。如果我們既要保持實例1的簡潔性,又要獲得 迭代效果,這時,yield 就派上用場了:

實例4

def fab(max): 
    n, a, b = 0, 0, 1 
    while n < max: 
        yield b 
        a, b = b, a + b 
        n = n + 1 

這時你就會發現,實例4與實例1的區別僅僅在於將中間的print換成了yield。


因此,簡單來說,yield就是把一個普通函數變成了一個生成器。
yield 的好處是顯而易見的,把一個函數改寫爲一個 generator 就獲得了迭代能力,比起用類的實例保存狀態來計算下一個 next() 的值,不僅代碼簡潔,而且執行流程異常清晰。

以上大致是今日所得,日後如果遇到相關問題,再對此文另行補充~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章