文章出處

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

迭代器

為了更好的理解迭代器和生成,我們需要簡單的回顧一下迭代器協議的概念。

迭代器協議 

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

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

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

for循環

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

for循環就是基于迭代器協議提供了一個統一的可以遍歷所有對象的方法,即在遍歷之前,先調用對象的__iter__方法將其轉換成一個迭代器,然后使用迭代器協議去實現循環訪問,這樣所有的對象就都可以通過for循環來遍歷了,

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

如訪問一個list,可以使用平時習慣的寫法:

#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循環。

 


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


全站熱搜
創作者介紹
創作者 大師兄 的頭像
大師兄

IT工程師數位筆記本

大師兄 發表在 痞客邦 留言(0) 人氣()