2013年5月4日 星期六

python yield 語法與 generator 物件介紹

    這篇介紹 yield 語法以及用來維護 yield 行為的 generator 物件。
    事實上,yield 和 return 很像,只是當函數呼叫 return 時,該函數 call stack (python 中是 frame) 就會被清除,程式主導權回到呼叫該函數的手上。 而 yield 會把程式主導權交給呼叫該函數的手上,但是他不會把函數的 call stack 清除,因此下次呼叫時,可以從上次未執行的部分開始執行,而不是重新建立一個新 stack。



def return_fun():
    a = 1
    b = 1
    return a

print return_fun()
print return_fun()

    上面這段程式碼,每次呼叫 return_fun 時,都是從 a = 1 開始執行,然後遇到 return 就結束了。所以執行流程像這樣

     當有函數裡面有用到 yield 這個關鍵字時,事情就變得很不一樣了。



def yield_fun():
    a = 1
    b = 1
    yield a
    yield b
    
print yield_fun()

    當你執行上面這段程式碼時,你會發現他回傳給你的居然是 generator 物件。那要怎麼執行 yield_fun 裡面的 code 呢? 答案是使用 generator 的 next 方法。


def yield_fun():
    a = 3
    b = 2
    yield a
    c = 4
    yield b
    
generator = yield_fun()
print generator.next()
print generator.next()

    當使用者第一次呼叫 generator 的  next 方法時,他會從 a = 3 開始執行,直到遇到 yield。python 會回傳 yield 後面接的東西(就像 return ),之後再把現在的 call stack( python 中是 frame),儲存在 generator 的 gi_frame 屬性中。

    當使用者第二次呼叫 generator 的  next 方法時,他不會建立一個新的 call stack,而是從  generator 的 gi_frame 屬性中取出之前的 call stack。因此,程式會從 c = 4 開始執行(不是從 a = 3喔,如果是 function 的話,是從 a = 3 開始執行)。直到遇到 yield 或是 return 為止。遇到 yield 表示下次還可以再呼叫一次 next 方法。遇到 return 則表示 這個 generator 已經沒東西了。無法再呼叫 next 了。執行流程像這樣。

    事實上, yield 除了可以把資料回傳給呼叫者外,他也可以從呼叫者接受資料。向下面的程式碼。


def yield_fun():
    a = 3
    b = 2
    
    b = yield a
    yield b
    

generator = yield_fun()
print generator.next()
print generator.send(8)

    上面這段程式碼, yield 除了把 a 的值回傳給呼叫者外,他還會從呼叫者那邊接受一個值,把 b 的值指派成呼叫者給的參數。

    generator 打破函數執行完後, call stack 就會被清除的限制(因為 generator 把 call stack 存在 gi_frame 屬性,所以下次執行時,還知道之前的狀態)。

沒有留言:

張貼留言