python中的迭代器和生成器學習筆記總結

生成器就是一個在行爲上和迭代器很是相似的對象.   是個對象!python

迭代,顧名思意就是不停的代換的意思,迭代是重複反饋過程的活動,其目的一般是爲了逼近所需目標或結果。每一次對過程的重複稱爲一次「迭代」,而每一次迭代獲得的結果會做爲下一次迭代的初始值。算法

 

迭代器就是用於迭代操做(for 循環)的對象。它像列表同樣能夠迭代獲取其中的每個元素,任何實現了 __next__ 方法(python2 是 next)的對象均可以稱爲迭代器。express

 

它與列表的區別在於,構建迭代器的時候,不像列表把全部元素一次性加載到內存,而是以一種延遲計算(lazy evaluation)方式返回元素,這正是它的優勢。好比列表含有中一千萬個整數,須要佔超過400M的內存,而迭代器只須要幾十個字節的空間。由於它並無把全部元素裝載到內存中,而是等到調用 next 方法時候才返回該元素(call by need 的方式),本質上 for 循環就是不斷地調用迭代器的next方法。函數

 

咱們自定義一個迭代器,以斐波那契數列爲例:lua

 

class Fib:spa

    def __init__(self):對象

        self.prev = 0內存

        self.curr = 1ci

    def __iter__(self):generator

        return self

    def __next__(self):

        value = self.curr

        self.curr += self.prev

        self.prev = value

        return value

>>> f = Fib()

>>> list(islice(f, 0, 10))

[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

Fib既是一個可迭代對象(由於它實現了 __iter__方法)又是一個迭代器(由於實現了 __next__方法)。實例變量 prev和 curr用戶維護迭代器內部的狀態。每次調用 next()方法的時候作兩件事:

 

爲下一次調用 next()方法修改狀態

爲當前此次調用生成返回結果

 

迭代器就像一個懶加載的工廠,等到有人須要的時候纔給它生成值返回,沒調用的時候就處於休眠狀態等待下一次調用。

 

生成器

然而在 Python 中還有一種函數,用關鍵字 yield 來返回值,這種函數叫生成器函數,函數被調用時會返回一個生成器對象,生成器本質上仍是一個迭代器,也是用在迭代操做中,所以它有和迭代器同樣的特性,惟一的區別在於實現方式上不同,後者更加簡潔

關係

生成器實際上是一種特殊的迭代器,不過這種迭代器更加優雅。它不須要再像上面的類同樣寫 __iter__()和 __next__()方法了,只須要一個 yiled關鍵字。 生成器必定是迭代器(反之不成立),所以任何生成器也是以一種懶加載的模式生成值

 

 

若是列表元素能夠按照某種算法推算出來,那咱們是否能夠在循環的過程當中不斷推算出後續的元素呢?這樣就沒必要建立完整的list,從而節省大量的空間。在Python中,這種一邊循環一邊計算的機制,稱爲生成器(Generator)

 

要建立一個generator,有不少種方法:

 

 

1.只要把一個列表生成式的[]改爲(),就建立了一個generator,又叫生成器表達式(generator expression)

 

>>> L = [x * x for x in range(10)]

>>> L

[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

>>> g = (x * x for x in range(10))

>>> g

<generator object <genexpr> at 0x104feab40>

 

打印出generator的每個元素呢?

 

若是要一個一個打印出來,能夠經過generator的next()方法:

>>> g.next()

0

>>> g.next()

1

>>> g.next()

4

......

 

不斷調用next()方法實在是太變態了,正確的方法是使用for循環,由於generator也是可迭代對象

>>> g = (x * x for x in range(10))

>>> for n in g:

...     print n

...

0

1

4

9

16

25

36

49

64

81

 

2.若是推算的算法比較複雜,能夠用函數來實現

 

若是一個函數定義中包含yield關鍵字,那麼這個函數就再也不是一個普通函數,而是一個generator.

 

最難理解的就是generator和函數的執行流程不同。函數是順序執行,遇到return語句或者最後一行函數語句就返回。而變成generator的函數,在每次調用next()的時候執行,遇到yield語句返回,再次執行時從上次返回的yield語句處繼續執行。

 

舉個簡單的例子(很清楚了),定義一個generator,依次返回數字1,3,5:

 

>>> def odd():

...     print 'step 1'

...     yield 1

...     print 'step 2'

...     yield 3

...     print 'step 3'

...     yield 5

...

>>> o = odd()  ----->返回的是一個生成器對象,此時函數體中的代碼並不會執行,只有顯示或隱示地調用next的時候纔會真正執行裏面的代碼。

>>> o.next()

step 1

1

>>> o.next()

step 2

3

>>> o.next()

step 3

5

>>> o.next()

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

StopIteration

 

能夠看到,odd不是普通函數,而是generator,在執行過程當中,遇到yield就中斷,下次又繼續執行。執行3次yield後,已經沒有yield能夠執行了,因此,第4次調用nex

 

把函數改爲generator後,咱們基本上歷來不會用next()來調用它,而是直接使用for循環來迭代:

 

例子:好比,著名的斐波拉契數列(Fibonacci),除第一個和第二個數外,任意一個數均可由前兩個數相加獲得:

 

1, 1, 2, 3, 5, 8, 13, 21, 34, ...

 

(1)用函數實現:

 

def fib(max):

    n, a, b = 0, 0, 1

    while n < max:

        print b

        a, b = b, a + b

        n = n + 1

 

 

輸出:

>>> fib(6)

1

1

2

3

5

8

 

(2)要把fib函數變成generator,只須要把print b改成yield b就能夠了:

 

 

def fib(max):

    n, a, b = 0, 0, 1

    while n < max:

        yield b

        a, b = b, a + b

        n = n + 1

 

輸出:

>>> fib(6)

<generator object fib at 0x104feaaa0>

 

把函數改爲generator後,咱們基本上歷來不會用next()來調用它,而是直接使用for循環來迭代:

 

>>> for n in fib(6):

...     print n

...

1

1

2

3

5

8

相關文章
相關標籤/搜索