Python的迭代器和生成器

先說迭代器,對於stringlistdicttuple等這類容器對象,使用for循環遍歷是很方便的。在後臺for語句對容器對象調用iter()函數,iter()是python的內置函數。iter()會返回一個定義了next()方法的迭代器對象,它在容器中逐個訪問容器內元素,next()也是python的內置函數。在沒有後續元素時,next()會拋出一個StopIteration異常,通知for語句循環結束。好比:python

>>> s = 'abc'
>>> it = iter(s)
>>> it
<str_iterator object at 0x7f71fefe9d68>
>>> next(it)
'a'
>>> next(it)
'b'
>>> next(it)
'c'
>>> next(it)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

上面說的都是python自帶的容器對象,它們都實現了相應的迭代器方法,那若是是自定義類須要遍歷怎麼辦?方法很簡單,對這個類AClass,實現一個__iter__(self)方法,使其返回一個帶有__next__(self)方法的對象就能夠了。若是你在AClass恰好也定義了__next__(self)方法(通常使用迭代器都會定義),那在__iter__裏只要返回self就能夠。廢話少說,先上代碼:函數

class Fib(object):
    def __init__(self, max):
        super(Fib, self).__init__()
        self.max = max

    def __iter__(self):
        self.a = 0
        self.b = 1
        return self

    def __next__(self):
        fib = self.a
        if fib > self.max:
            raise StopIteration
        self.a, self.b = self.b, self.a + self.b
        return fib

def main():
    fib = Fib(100)
    for i in fib:
        print(i)

if __name__ == '__main__':
    main()

簡單講下代碼會幹什麼,定義了一個Fib類,用於生成fibonacci序列。用for遍歷時會逐個打印生成的fibonacci數,max是生成的fibonacci序列中數字大小的上限。工具

在類的實現中,定義了一個__iter__(self)方法,這個方法是在遍歷時被iter()調用,返回一個迭代器。由於在遍歷的時候,是直接調用的python內置函數iter(),由iter()經過調用__iter__(self)得到對象的迭代器。有了迭代器,就能夠逐個遍歷元素了。而逐個遍歷的時候,也是使用內置的next()函數經過調用對象的__next__(self)方法對迭代器對象進行遍歷。因此要實現__iter__(self)__next__(self)。並且由於實現了__next__(self),因此在實現__iter__(self)的時候,直接返回self就能夠。code

爲了更好理解,我再簡單重複下上面說的那一段:在循環遍歷自定義容器對象時,會使用python內置函數iter()調用遍歷對象的__iter__(self)得到一個迭代器,以後再循環對這個迭代器使用next()調用迭代器對象的__next__(self)__iter__只會被調用一次,而__next__會被調用 n 次。對象

下面說生成器。內存

生成器(Generator)是建立迭代器的簡單而強大的工具。它們寫起來就像是正規的函數,只是在須要返回數據的時候使用yield語句。每次next()被調用時,生成器會返回它脫離的位置(它記憶語句最後一次執行的位置和全部的數據值)。如下示例演示了生成器能夠很簡單的建立出來:ci

>>> def reverse(data):
...     for index in range(len(data)-1, -1, -1):
...         yield data[index]
... 
>>> for char in reverse('hello'):
...     print(char)
... 
o
l
l
e
h

關於迭代器和生成器的區別,生成器能作到迭代器能作的全部事,並且由於自動建立了__iter__()next()方法,生成器顯得特別簡潔,並且生成器也是高效的。除了建立和保存程序狀態的自動方法,當發生器終結時,還會自動拋出StopIteration異常。一個帶有yield的函數就是一個 生成器,它和普通函數不一樣,生成一個 generator 看起來像函數調用,但不會執行任何函數代碼,直到對其調用next()(在 for 循環中會自動調用next())纔開始執行。雖然執行流程仍按函數的流程執行,但每執行到一個yield語句就會中斷,並返回一個迭代值,下次執行時從yield的下一個語句繼續執行。看起來就好像一個函數在正常執行的過程當中被yield中斷了數次,每次中斷都會經過yield返回當前的迭代值(yield暫停一個函數,next()從其暫停處恢復其運行)。generator

另外對於生成器,python還提供了一個生成器表達式:相似與一個yield值的匿名函數。表達式自己看起來像列表推到, 但不是用方括號而是用圓括號包圍起來:string

>>> unique_characters = {'E', 'D', 'M', 'O', 'N', 'S', 'R', 'Y'}
>>> gen = (ord(c) for c in unique_characters)
>>> gen
<generator object <genexpr> at 0x7f2be4668678>
>>> for i in gen:
...     print(i)
... 
69
79
83
77
82
78
89
68
>>>

若是須要,能夠將生成器表達式傳給tuplelist或是set來迭代全部的值而且返回元組、列表或是集合。在這種狀況下,不須要一對額外的括號 ———— 直接將生成器表達式 ord(c) for c in unique_characters傳給tuple()等函數就能夠了, Python 會推斷出它是一個生成器表達式。it

最後,爲何要使用生成器?由於效率。使用生成器表達式取代列表解析能夠同時節省 cpu 和 內存(ram)。若是你構造一個列表的目的僅僅是傳遞給別的函數,(好比 傳遞給tuple()或者set()), 那就用生成器表達式替代吧!

相關文章
相關標籤/搜索