1. 程式人生 > >Python生成器:yield的使用

Python生成器:yield的使用

生成器概述

通過列表生成式,我們可以直接建立一個列表。但是,受到記憶體限制,列表容量肯定是有限的。而且,建立一個包含100萬個元素的列表,不僅佔用很大的儲存空間,如果我們僅僅需要訪問前面幾個元素,那後面絕大多數元素佔用的空間都白白浪費了。

所以,如果列表元素可以按照某種演算法推算出來,那我們是否可以在迴圈的過程中不斷推算出後續的元素呢?這樣就不必建立完整的list,從而節省大量的空間在Python中,這種一邊迴圈一邊計算的機制,稱為生成器(Generator)。

生成器是一個包含了特殊關鍵字yield的函式。當被呼叫的時候,生成器函式返回一個生成器。可以使用send,throw,close方法讓生成器和外界互動。

生成器也是迭代器,但是它不僅僅是迭代器,擁有next方法並且行為和迭代器完全相同。所以生成器也可以用於python的迴圈中。

生成器是這樣一個函式,它記住上一次返回時在函式體中的位置。對生成器函式的第二次(或第 n 次)呼叫跳轉至該函式中間,而上次呼叫的所有區域性變數都保持不變。

生成器不僅“記住”了它的資料狀態;生成器還“記住”了它在流控制構造(在指令式程式設計中,這種構造不只是資料值)中的位置。

生成器的特點

生成器是一個函式,而且函式的引數都會保留。

迭代到下一次的呼叫時,所使用的引數都是第一次所保留下的,即是說,在整個所有函式呼叫的引數都是第一次所呼叫時保留的,而不是新建立的。

yield 生成器的執行機制

當你問生成器要一個數時,生成器會執行,直至出現 yield 語句,生成器把 yield 的引數給你,之後生成器就不會往下繼續執行。 當你問他要下一個數時,他會從上次的狀態開始執行,直至出現yield語句,把引數給你,之後停下。如此反覆,直至退出函式。

在python中,當你定義一個函式,使用了yield關鍵字時,這個函式就是一個生成器,它的執行會和其他普通的函式有很多不同,函式返回的是一個物件,而不是你平常所用return語句那樣,能得到結果值。如果想取得值,得呼叫next()函式

例項

例子一:

#! /usr/bin/env python
#coding=utf-8
def fib(): a,b = 0,1 while 1: yield b a,b = b,a+b #yield a if __name__ == '__main__': f = fib() #函式返回的是一個物件 for i in range(6): print f.next()#呼叫next()函式取得值

例子二:

def fib(max):  
    a, b = 1, 1  
    while a < max:  
        yield a 
        a, b = b, a+b  

程式執行:

for n in fib(15):  
    print n  

從前面的執行機制描述中,可以獲知,程式執行到yield這行時,就不會繼續往下執行。而是返回一個包含當前函式所有引數狀態的iterator物件。目的就是為了第二次被呼叫時,能夠訪問到函式所有的引數值都是第一次訪問時的值,而不是重新賦值
程式第一次呼叫時:

def fib(max):  
    a, b = 1, 1  
    while a < max:  
        yield a #這時a,b值分別為1,1,當然,程式也在執行到這時,返回  
        a, b = b, a+b  

程式第二次呼叫時:
從前面可知,第一次呼叫時,a,b=1,1,那麼,我們第二次呼叫時(其實就是呼叫第一次返回的iterator物件的next()方法),程式跳到yield語句處
執行a,b = b, a+b語句,此時值變為:a,b = 1, (1+1) => a,b = 1, 2

程式繼續while迴圈,當然,再一次碰到了yield a 語句,也是像第一次那樣,儲存函式所有引數的狀態,返回一個包含這些引數狀態的iterator物件。

等待第三次的呼叫….

通過上面的分析,可以依次類推的展示了yield的詳細執行過程!

而將上述過程用迭代器實現如下:

#! /usr/bin/env python
#coding=utf-8

class Fib:  
    def __init__(self, max):  
        self.max = max  
    def __iter__(self):  
        self.a = 0  
        self.b = 1  
        return self  
    def next(self):  
        fib = self.a  
        if fib > self.max:  
            raise StopIteration  
        self.a, self.b = self.b, self.a + self.b  
        return fib  

if __name__ == '__main__':

    for f in Fib(6):
        print f

結果:

0
1
1
2
3
5

很顯然,沒有生成器簡潔,通過使用生成器的語法,可以免去寫迭代器類的繁瑣程式碼。