Python Generators(生成器)

張博208發表於2017-02-19

ython] view plain copy
 print?
  1. <span style="font-family: Arial, Verdana, sans-serif; white-space: normal; ">Python Generators(<span style="font-family:宋體;">生成器</span><span style="font-family:Times New Roman;">)</span></span>  

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

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

生成器的特點

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

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

Python中,yield就是這樣的一個生成器。


yield 生成器的執行機制

當你問生成器要一個數時,生成器會執行,直至出現 yield 語句,生成器把 

     yield 的引數給你,之後生成器就不會往下繼續執行。 當你問他要下一個數時,他會從上次的狀態。開始執行,直至出現yield語句,把引數給你,之後停下。如此反覆直至退出函式。(以上關於yield的描述,在後面列舉一個簡單的例子來解釋這段話)

yield的使用

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

[python] view plain copy
 print?
  1. c = h() #h()包含了yield關鍵字  
  2. #返回值  
  3. c.next()  

每當呼叫一次迭代器的next函式,生成器函式執行到yield之處,返回yield後面的值且在這個地方暫停,所有的狀態都會被保持住,直到下次next函式被呼叫,或者碰到異常迴圈退出。

下面,來看看以下的例子程式碼吧,是用來說明yield執行機制的。

[python] view plain copy
 print?
  1. def fib(max):  
  2.     a, b = 11  
  3.     while a < max:  
  4.         yield a #generators return an iterator that returns a stream of values.  
  5.         a, b = b, a+b  

程式執行:

[python] view plain copy
 print?
  1. for n in fib(15):  
  2.     print n  

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

程式第一次呼叫時:

[python] view plain copy
 print?
  1. def fib(max):  
  2.     a, b = 11  
  3.     while a < max:  
  4.         yield a #這時a,b值分別為1,1,當然,程式也在執行到這時,返回  
  5.         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物件。

等待第三次的呼叫....


[python] view plain copy
 print?
  1. def fib(max):  
  2.     a, b = 11  
  3.     while a < max:  
  4.         yield a   
  5.         a, b = b, a+b  


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

通過使用生成器的語法,可以免去寫迭代器類的繁瑣程式碼,如,上面的例子使用迭代類來實現,程式碼如下:

[python] view plain copy
 print?
  1. class Fib:  
  2.     def __init__(self, max):  
  3.         self.max = max  
  4.     def __iter__(self):  
  5.         self.a = 0  
  6.         self.b = 1  
  7.         return self  
  8.     def next(self):  
  9.         fib = self.a  
  10.         if fib > self.max:  
  11.             raise StopIteration  
  12.         self.a, self.b = self.b, self.a + self.b  
  13.         return fib  


yield其他例子展示:排列,組合

#生成全排列    

[python] view plain copy
 print?
  1. def perm(items, n = None):  
  2.     if n is None:  
  3.         n = len(items)  
  4.     for i in range(len(items)):  
  5.         v = items[i:i+1]  
  6.         if n==1:  
  7.             yield v  
  8.         else:  
  9.             rest = items[:i] + items[i+1:]  
  10.             for p in perm(rest, n-1):  
  11.                 yield v + p  
  12. def comb(items, n = None):  
  13.     if n is None:  
  14.         n = len(items)  
  15.     else:  
  16.         for i in range(len(items)):  
  17.             v = items[i:i+1]  
  18.             if 1 == n:  
  19.                 yield v  
  20.             else:  
  21.                 rest = items[i+1:]  
  22.                 for c in comb(rest, n-1):  
  23.                     yield v + c  


相關文章