Python的迭代器與生成器

Python中的生成器和迭代器方便好用,可是平時對生成器和迭代器的特性掌握的不是很到位,今天將這方面的知識整理一下。python

迭代器

爲了更好的理解迭代器和生成,咱們須要簡單的回顧一下迭代器協議的概念。算法

迭代器協議 

1.迭代器協議是指:對象必須提供一個next方法,執行該方法要麼返回迭代中的下一項,要麼就引發一個StopIteration異常,以終止迭代 (只能日後走不能往前退)編程

2.可迭代對象:實現了迭代器協議的對象(如何實現:對象內部定義一個__iter__()方法)函數

3.協議是一種約定,可迭代對象實現了迭代器協議,python的內部工具(如for循環,sum,min,max函數等)使用迭代器協議訪問對象。工具

for循環

for循環的本質:循環全部對象,全都是使用迭代器協議。spa

for循環就是基於迭代器協議提供了一個統一的能夠遍歷全部對象的方法,即在遍歷以前,先調用對象的__iter__方法將其轉換成一個迭代器,而後使用迭代器協議去實現循環訪問,這樣全部的對象就均可以經過for循環來遍歷了,code

列表,字符串,元組,字典,集合,文件對象等本質上來講都不是可迭代對象,在使用for循環的時候內部是先調用他們內部的_iter_方法,使他們變成了可迭代對象,而後在使用可迭代對象的_next_方法依次循環元素,當元素循環完時,會觸發StopIteration異常,for循環會捕捉到這種異常,終止迭代。對象

如訪問一個list,能夠使用平時習慣的寫法:blog

#for循環訪問
#for循環l本質就是遵循迭代器協議的訪問方式,先調用diedai_l=l.__iter__()方法,或者直接diedai_l=iter(l),而後依次執行diedai_l.next(),直到for循環捕捉到StopIteration終止循環
li = [1,2,3,4]
for i in li:#li_iter = li._iter_()
    print(i)#li_iter._next_

也能夠直接使用迭代器訪問:內存

#迭代器協議訪問
li = [1,2,3,4]
f = li.__iter__()#第一步,先經過內部的_iter_方法,先把對象變成可迭代對象
print(f.__next__())#對可迭代對象用_next_方法取值
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())#StopIteration,超出邊界會報錯

生成器

在介紹生成器以前,先簡單介紹一下列表生成式

列表生成式

列表生成式即List Comprehensions,是Python內置的很是簡單卻強大的能夠用來建立list的生成式。

舉個例子,要生成list[1,2,3,4,5,6,7,8,9,10]能夠用range(1,11):

>>> range(1, 11)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

列表生成式能夠代替循環在編程中偷懶,如生成[1x1, 2x2, 3x3, ..., 10x10]怎麼作?能夠用普通的循環,也能夠用列表生成器完成,以下:

>>> [x * x for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

經過列表生成式,咱們能夠直接建立一個列表。可是,受到內存限制,列表容量確定是有限的。並且,建立一個包含100萬個元素的列表,不只佔用很大的存儲空間,若是咱們僅僅須要訪問前面幾個元素,那後面絕大多數元素佔用的空間都白白浪費了。

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

建立生成器的兩種方法

第一種方法很簡單,只要把一個列表生成式的[]改爲(),就建立了一個generator:

>>> 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>

L是一個list,而g是一個generator,若是想要訪問生成器中元素,須要用生成器的next()方法。或者利用for循環,由於generator也是一個可迭代的對象。

第二種方法須要藉助「yield」,以計算斐波那契數列爲例,展現一個函數如何變成生成器,直接上代碼:

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

這是普通的函數,將print改成yield即爲生成器:

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

函數是順序執行,遇到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()
>>> 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次調用next()就報錯。

一樣,在獲取元素時,大多數時候運用for循環。

相關文章
相關標籤/搜索