1. 程式人生 > 其它 >Python 迭代器+生成器+裝飾器

Python 迭代器+生成器+裝飾器

一、迭代器

1、迭代器協議

  • 迭代器協議是指:物件必須提供一個next方法,執行該方法要麼返回迭代中的下一項,要麼就引起一個stoplteration異常,以終止迭代(只能 往後走,不能往前退
  • 協議是一種約定,可迭代物件實現了迭代器協議,python的內部工具(如for迴圈,sum,min,max函式等)使用迭代器協議訪問物件

2、可迭代物件

可迭代物件泛指一類物件,不是指的每一種物件,確切的說滿足以下的條件的物件可以成為可迭代物件:

  • 物件實現了__iter__方法
  • __iter__方法返回了一個迭代器物件

我們比較容易理解的可迭代物件,比如說可以用for語句去遍歷,實際for語句的內部實現應該就是首先呼叫物件的__iter__方法,獲取一個迭代器物件,接著不停的呼叫迭代器物件的__next__方法,迴圈遍歷取值。

3、迭代器物件(迭代器)

迭代器協議包括這些條件:

  • 物件實現了__next__方法
  • __next__方法返回了某個數值(當然一般情況下,我們需要的是返回這個物件的特定的數字,並且按照一定的順序進行依次返回)
  • __next__方法需要在值取完的時候,丟擲StopIteration的錯誤資訊。

能夠迭代的型別:list tuple string set dict bytes迭代器有兩個基本的方法:iter()next()

  • iter返回迭代器物件本身。這用於for 和in語句。
  • next方法返回迭代器中的下一個值。如果沒有更多的專案要返回,那麼它應該引發StopIteration異常。

建立一個返回數字的迭代器,初始值為 1,逐步遞增 1:

class MyNumbers:
  def __iter__(self):
    self.a = 1
    return self
 
  def __next__(self):
    x = self.a
    self.a += 1
    return x
 
myclass = MyNumbers()
myiter = iter(myclass)
 
print(next(myiter))
print(next(myiter))
print(next(myiter))
print(next(myiter))
print
(next(myiter))

輸出:

1
2
3
4
5

二、生成器

可以理解為一種資料型別,這種資料型別自動實現了迭代器協議(其他的資料型別需要呼叫自己的內建的__iter__方法),所以生成器就是可迭代物件

1、生成器函式

常規函式的定義,但是,使用yield語句而不是return語句返回結果。yield語句語句一次返回一個結果,在每個結果中間,掛起函式的狀態,以便下次從它離開的地方繼續執行

2、生成器表示式

類似於列表推導,但是,生成器返回按需產生結果的一個物件,而不是一次構建一個結果列表

3、生成器的特點

生成器最大的特點是:邊迭代 邊輸出

Python使用生成器對延遲操作提供了支援。所謂延遲操作,是指在需要的時候才產生結果,而不是立即產生結果。這也是生產層器的主要好處。

這裡舉一個例子來說明生成器的優點

輸出斐波那契數列前 N 個數

def fab(max): 
    n, a, b = 0, 0, 1 
    L = [] 
    while n < max: 
        L.append(b) 
        a, b = b, a + b 
        n = n + 1 
    return L
 
for n in fab(5): 
    print n

該函式在執行中佔用的記憶體會隨著引數 max 的增大而增大,如果要控制記憶體佔用,最好不要用 List來儲存中間結果,而是通過 iterable 物件來迭代。

class Fab(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 = self.n + 1 
            return r 
        raise StopIteration()
 
for n in Fab(5): 
    print n

Fab 類通過 next() 不斷返回數列的下一個數,記憶體佔用始終為常數。

然而,使用 class 改寫的這個版本,程式碼遠遠沒有第一版的 fab 函式來得簡潔。如果我們想要保持第一版 fab 函式的簡潔性,同時又要獲得 iterable 的效果,yield 就派上用場了:

def fab(max): 
    n, a, b = 0, 0, 1 
    while n < max: 
        yield b      # 使用 yield
        # print b 
        a, b = b, a + b 
        n = n + 1
 
for n in fab(5): 
    print n

簡單地講,yield 的作用就是把一個函式變成一個 generator,帶有 yield 的函式不再是一個普通函式,Python 直譯器會將其視為一個 generator,呼叫 fab(i) 不會執行 fab 函式,而是返回一個 iterable 物件!在 for 迴圈執行時,每次迴圈都會執行 fab 函式內部的程式碼,執行到 yield b 時,fab 函式就返回一個迭代值,下次迭代時,程式碼從 yield b 的下一條語句繼續執行,而函式的本地變數看起來和上次中斷執行前是完全一樣的,於是函式繼續執行,直到再次遇到 yield。

一個帶有 yield 的函式就是一個 generator,它和普通函式不同,生成一個 generator 看起來像函式呼叫,但不會執行任何函式程式碼,直到對其呼叫 next()(在 for 迴圈中會自動呼叫 next())才開始執行。雖然執行流程仍按函式的流程執行,但每執行到一個 yield 語句就會中斷,並返回一個迭代值,下次執行時從 yield 的下一個語句繼續執行。看起來就好像一個函式在正常執行的過程中被 yield 中斷了數次,每次中斷都會通過 yield 返回當前的迭代值。

yield 的好處是顯而易見的,把一個函式改寫為一個 generator 就獲得了迭代能力,比起用類的例項儲存狀態來計算下一個 next() 的值,不僅程式碼簡潔,而且執行流程異常清晰。

這裡記錄一個引出本篇部落格的問題:

Scrapy中yield的理解

https://www.oschina.net/question/2254016_238539

三、裝飾器