Python迭代器生成器

  在學習python數據結構的過程中,可迭代對象,迭代器,生成器這些概念參雜在一起,難免讓初學者一頭霧水,今天就來捋捋這些概覽。

可迭代對象(iterable)

  什麼是可迭代對象,通俗的講就是可以直接通過for循環遍歷的對象就可稱爲可迭代對象Iterable,可以使用isinstance()判斷一個對象是否是Iterable對象:

>>>from collections import Iterable
>>>isinstance([], Iterable)
True
>>>isinstance({}, Iterable)
True
>>>isinstance('123', Iterable)
True
>>>isinstance(123, Iterable)
False

可迭代對象並不指某種具體的數據類型,list, dict, set, str都是迭代對象,再比如打開狀態的files,sockets也是可迭代對象,可迭代對象是指代對象的一種屬性,代表該對象是可迭代的。可迭代對象實現了__iter__方法,該方法返回一個迭代器對象。

迭代器(iterator)

  任何實現了__iter__和__next__方法的對象都是迭代器(python2是實現__iter__和next方法),__iter__返回迭代器自身,__next__返回容器中的下一個值,如果容器中沒有更多元素了,則拋出StopIteration異常。可以使用isinstance()判斷一個對象是否是Iterator對象:

>>>from collections import Iterator
>>>isinstance([], Iterator)
False
>>>isinstance({}, Iterator)
False
>>>isinstance('123', Iterator)
False
>>>isinstance((x for x in range(10)), Iterator)
True

其中(x for x in range(10))是生成器表達式,它返回的是一個生成器對象,不同於列表生成式[x for x in range(10)]返回一個list對象。生成器對象都是迭代器對象,但list, dict, str雖然是可迭代對象,但不是迭代器對象,可以使用iter()將list, dict, str等可迭代對象變成迭代器對象。

>>>isinstance(iter([]), Iterator)
True
>>>isinstance(iter('123'), Iterator)
True

python的迭代器對象表示一個數據流,可以將這個數據流看作一個有序序列,但我們並不知道序列的長度,只能不斷通過調用next()函數實現按需計算下一個數據,因此迭代器的計算是惰性的,只有在需要返回下一個數據時它才計算,迭代器的這種特性可以大大減少內存的開銷,迭代器對象甚至可以表示一個無限大的數據流,而讓list, dict或者str存儲一個無限大的數據流是不可能的。

下面我們通過迭代器來實現斐波那契數列:

from collections import Iterable
from collections import Iterator

class Fib:
    def __init__(self, max):
        self.n, self.max = 0, max
        self.a, self.b = 0, 1

    def __iter__(self):
        return self

    def __next__(self):
        if self.n < self.max:
            self.n += 1
            self.a, self.b = self.b, self.a + self.b
            return self.a
        else:
            raise StopIteration

if __name__ == '__main__':
    fib = Fib(10)
    print(isinstance(fib, Iterable)) # True
    print(isinstance(fib, Iterator)) # True
    print([e for e in fib]) # [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

  Fib既是一個可迭代對象(因爲它實現了__iter__方法),又是一個迭代器(因爲實現了__next__方法)。實例變量a和b用於維護迭代器內部的狀態。每次調用next()方法的時候做兩件事:
爲下一次調用next()方法修改狀態,爲當前這次調用生成返回結果。

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

生成器(generator)

  生成器是一種特殊的迭代器,不過這種迭代器更加優雅。它不需要再像上面的類一樣寫__iter__()和__next__()方法了,只需要一個yiled關鍵字。

用生成器來實現斐波那契數列的例子是:

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

f = fib(10)
print(f) # <generator object fib at 0x10d8cf888>
print([e for e in f])   # [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

fib函數中的yield關鍵字,將該函數變成了一個生成器,當執行f=fib(10)返回的是一個生成器對象,此時函數中的代碼並不會執行,只有顯示或隱示地調用next的時候纔會真正執行裏面的代碼,在每次調用next()方法時,遇到yield語句返回值並中斷,再次執行時從上次返回的yield語句處繼續執行。
生成器是python非常強大的特性,相比其他容器對象它更加節省內存,同時使用更少的代碼,使你的代碼更加的優雅,凡事以下結構都可以通過生成器重構:

def fun():
    result = []
    for ... in ...:
        result.append(x)
    return result

def fun_gen():
    for ... in ...:
        yield x

總結

  1. 可迭代對象實現了__iter__方法,該方法返回一個迭代器對象。
  2. 迭代器持有一個內部狀態的字段,用於記錄下次迭代返回值,它實現了__next__和__iter__方法,迭代器不會一次性把所有元素加載到內存,而是需要的時候才生成返回結果。
  3. 生成器是一種特殊的迭代器,它的返回值不是通過return而是用yield。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章