python 迭代器與生成器

參考:
1.http://python.jobbole.com/81916/
2.http://blog.csdn.net/bluebird_237/article/details/38894617
3.http://www.runoob.com/python3/python3-iterator-generator.html

迭代器

先了解幾個概念:

可以直接作用於for循環的對象統稱爲可迭代對象(Iterable)。

可以被next()函數調用並不斷返回下一個值的對象稱爲迭代器(Iterator)。

所有的Iterable均可以通過內置函數iter()來轉變爲Iterator。

對於迭代器而言,通過next()函數實現“迭代”這一概念,在使用for in語句的時候,程序就會自動調用即將被處理的對象的迭代器對象,然後使用它的next方法,當長度超過了迭代器長度時,就會報StopIteration的異常。

>>> a=[1,2,3]
>>> for i in a:
...     print(i)
...
1
2
3
>>> next(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: list object is not an iterator
>>> I=iter(a)
>>> for i in I:
...     print i
...
1
2
3
>>> next(I)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

有以下結論:
1.內置函數iter()僅僅是調用了對象的iter()方法,所以list對象內部一定存在方法iter()

2.內置函數next()僅僅是調用了對象的next()方法,所以list對象內部一定不存在方法next__(),但是Itrator中一定存在這個方法

3.for循環內部事實上就是先調用iter()把Iterable變成Iterator在進行循環迭代的

可以通過下面這個例子進一步說明:

>>> a=[1,2,3]
>>> I=a.__iter__()
>>> a.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute 'next'
>>> I.next()
1
>>> from collections import Iterator, Iterable
>>> isinstance(a,Iterator)
False
>>> isinstance(a,Iterable)
True
>>> isinstance(I,Iterable)
True
>>> isinstance(I,Iterator)
True

4.Iterator繼承自Iterable,從下面的測試中可以很方便的看到Iterator包含iter()和next()方法,而Iteratble僅僅包含iter()。

>>> from collections import Iterator, Iterable
>>> help(Iterator)
Help on class Iterator:

class Iterator(Iterable)
 |  Method resolution order:
 |      Iterator
 |      Iterable
 |      builtins.object   
 |**註解:從這裏可以看出Iterable繼承自object, Iterator繼承自Iterable。
 |  Methods defined here:
 |
 |  __iter__(self)
 |
 |  __next__(self)
 |      Return the next item from the iterator. When exhausted, raise StopIteration
......
>>> help(Iterable)
Help on class Iterable:

class Iterable(builtins.object)
 |  Methods defined here:
 |
 |  __iter__(self)
......

還有一個需要注意的地方,那就是迭代器是一次性消耗品,使用完了就無法倒退,因此如果需要多次使用需要進行復制。

>>> I=iter([1,2,3])
>>> for i in I:
...     pass
...
#此時迭代器I已消耗完
>>> next(L)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'L' is not defined
#然而一開始的複製並沒有起作用

如何解決呢?
python2.x似乎只能通過重新生成來實現

生成器

在講生成器之前,我們先講講如何使得自己自定義的類變爲Iterable
通過help(iter)查看一下:

iter(...)
    iter(collection) -> iterator
    iter(callable, sentinel) -> iterator

    Get an iterator from an object. 
    In the first form, the argument must supply its own iterator, or be a sequence.
    In the second form, the callable is called until it returns the sentinel.

從說明中我們可以看出,直接使用iter函數返回迭代器,需要對象能調用自身的iter函數得到iterator或者是一個序列(序列化)。

序列化相關的內容,博主還不太瞭解以後學習深入了可以回來補一下。
在不做序列化處理的條件下,實現自定義類的可iter(),需要自己實現iter()函數。

class Iterable:
    def __iter__(self):
        return Iterator()

class Iterator:
    def __init__(self):
        self.start=-1
    def next(self):
        self.start +=2
        return self.start

I = Iterable()
for count, i in zip(range(5),I):  
    print(i)
輸出:13579

上面的代碼中也可以將兩個類合在一起寫,這到無關緊要。

到目前爲止,我們得到迭代器都是通過iter()函數轉化原對象爲iterator,並調用next方法實現迭代操作。
如果我們只是單純的想做一些迭代的操作(可以節省內存),如不斷去計算類裏面一些值並結果,而不想把它當做類的next()方法又該如何呢?
這個時候就需要生成器了。

在 Python 中,使用了 yield 的函數被稱爲生成器(generator)。
跟普通函數不同的是,生成器是一個返回迭代器的函數,只能用於迭代操作。
在調用生成器運行的過程中,每次遇到 yield 時函數會暫停並保存當前所有的運行信息,返回yield的值。並在下一次執行 next()方法時從當前位置繼續運行。
舉個例子,我們希望有一個基於菲波那切數列的循環操作
1.通過控制條件,實現變量的斐波那契額變化
這樣說可以實現的,但是複用性太差,每次如果操作改了都得重新再做一次菲波那切變化,並寫函數體

2.獲得這樣一個菲波那切數列並通過for in迭代,但是如果該數列較大,佔用內存也較大

3.得到一個可迭代對象,next()函數符合菲波那切變化
這樣的一種方式比較好,如果通過之前的說法我們最起碼得自定義一個類實現next()方法,但這樣的工作實在沒必要,爲此python通過yield實現了將所需函數轉化爲對應next()函數,並返回iterator對象的操作,這樣一來相當於你只需要寫next()函數的內容便可以輕鬆得到自定義的迭代器。

def fibonacci(n): # 生成器函數 - 斐波那契
    a, b, counter = 0, 1, 0
    while True:
        if (counter > n): 
            return
        yield a
        a, b = b, a + b
        counter += 1
f = fibonacci(10) # f 是一個迭代器,由生成器返回生成

while True:
    try:
        print next(f)
    except StopIteration:
        print("done")
        break
輸出:0,1,1,2,3,5,8,13,21,34,55
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章