Python中的迭代器與生成器

container.__iter__()  和 iterator.__next__()

迭代器就是一個有 __next__() 方法的對象。當須要下一個數據時,調用它的 __next__() 方法就能夠得到。在Python2中,這個方法被命名爲 next() 。但在Python3中新增長了內建的 next() 函數,就像用來調用 __iter__() 的 iter() 函數同樣,next() 也是用來調用 __next__() 的。 python

就如上面所說,對迭代器來說,有一個__next__()就夠了。在你使用for 和 in 語句時,若是可行,程序就會自動調用即將被處理的對象的迭代器對象,而後使用它的__next__()方法,直到監測到一個StopIteration異常。以比較經常使用的 range() 來舉例: shell

>>> type(range(10))
<class 'range'>
>>> next(range(10))
Traceback (most recent call last):
  File "<pyshell#163>", line 1, in <module>
    next(range(10))
TypeError: 'range' object is not an iterator
>>> type(iter(range(10)))
<class 'range_iterator'>
>>> next(iter(range(10)))
0

咱們能夠看到,range(10) 自己做爲一個 <class 'range'> 對象是不可迭代的,可是 iter(range(10)) 或者說 range(10).__iter__() 能夠,它的類(型)是<class 'range_iterator'>,它有 __next__() 方法。那麼如何使用迭代器看起來就變得簡單了——咱們只要在類裏面定義一個 __iter__() 函數,用它來返回一個帶 __next__() 方法的對象就夠了。其實按照官方文檔的說法,迭代器對象裏也應該有一個 __iter__() 方法,這個方法只須要一個語句 return self ,這是爲了保證在使用 for 語句時,容器對象和迭代器對象均可以被正確調用,就像下面這樣: 函數

>>>for x in container:pass 
>>>for x in iter(container):pass

__iter__() 的設計很是簡單,由於咱們大能夠把 self 返回,而後在類裏面定義 __next__() 。這樣定義的一個類,其自身既是容器,又是迭代器(真棒)。那麼接下來的主要問題就是設計 __next__() 了。 設計

咱們使用斐波那契數列來舉例: code

class Feb:
    def __init__(self):
        self.pre = 1
        self.num = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.num < 10:
            self.pre,self.num = self.num,self.pre+self.num
            return self.num
        else:raise StopIteration

試運行以下: 對象

>>> feb = Feb()
>>> for i in feb:
	print(i)

1	
1
2
3
5
8
13
>>>

嗯,還算正常。for 循環正確迭代了 Feb 實例,正確處理了 StopIteration 異常。只是有一個小Bug,我原本沒想讓它輸出13來着… 文檔

咱們能夠看到這個 __next__() 實現的不很好,它看起來很麻煩。那麼如何「優雅」地實現迭代器呢?固然就是用「生成器」啦! generator

generator.__next__() 和 generator.send() 和 generator.close()

生成器是一個特定的「函數」,當調用時它返回一個生成器對象(因此你不能在定義生成器的時候 return 任何值,但能夠使用單獨的一個 return ,它表明進度結束)。生成器容許你返回一個值,而後暫停代碼的執行,稍後恢復,能夠這樣重複 n 次。實現這一「優雅」效果的關鍵字就是 yield it

仍是斐波那契數列: io

def feb():
    pre = num = 1
    yield pre
    yield num
    while True:
        pre, num = num, pre+num
        if num<10:
            yield num
        else:return

輸出以下:嗯,此次沒有10以上。

>>> a = feb()
>>> for i in a:
	print(i)

	
1
1
2
3
5
8
>>>

值得一提的是,這種協同程序是能夠在 yield 值出來的時候順便回傳一個參數(或異常)的,它定義時的語法是:foo = (yield bar) 。當程序把 bar 返回出來的時候,程序掛起。這時能夠經過使用 generator.send() 方法來給實例裏的foo賦值。若是你不想再迭代了,還能夠使用 generator.close() 方法來關閉生成器。

下面是一個從可由用戶定義的整數 n 開始不斷返回 n+1 的生成器:

def plus1(n=0):
    n = n
    while True:
        var = (yield n)
        if var:
            n = var
        else:n += 1

>>> a = plus1()

>>> for i in a: if i>3:break else:print(i) 0 1 2 3 >>> a.send(100) 100 >>> for i in a: if i > 103:break else:print(i) 101 102 103 >>> a.close() >>> next(a) Traceback (most recent call last): File "<pyshell#230>", line 1, in <module> next(a) StopIteration >>>
相關文章
相關標籤/搜索