參考鏈接: Python | 可迭代和迭代器之間的區別
本篇文章簡單談談可迭代對象,迭代器和生成器之間的關系。
?
?
三者簡要關系圖
?
?
?
?
?
可迭代對象與迭代器
?
剛開始我認為這兩者是等同的,但后來發現并不是這樣;下面直接拋出結論:
1)可迭代對象包含迭代器。2)如果一個對象擁有__iter__方法,其是可迭代對象;如果一個對象擁有next方法,其是迭代器。3)定義可迭代對象,必須實現__iter__方法;定義迭代器,必須實現__iter__和next方法。
?
?
你也許會問,結論3與結論2是不是有一點矛盾?既然一個對象擁有了next方法就是迭代器,那為什么迭代器必須同時實現兩方法呢?
因為結論1,迭代器也是可迭代對象,因此迭代器必須也實現__iter__方法。
?
?
介紹一下上面涉及到的兩個方法:
1)__iter__()
該方法返回的是當前對象的迭代器類的實例。因為可迭代對象與迭代器都要實現這個方法,因此有以下兩種寫法。
寫法一:用于可迭代對象類的寫法,返回該可迭代對象的迭代器類的實例。
寫法二:用于迭代器類的寫法,直接返回self(即自己本身),表示自身即是自己的迭代器。
也許有點暈,沒關系,下面會給出兩寫法的例子,我們結合具體例子看。
?
2)next()
?
返回迭代的每一步,實現該方法時注意要最后超出邊界要拋出StopIteration異常。
?
下面舉個可迭代對象與迭代器的例子:
?
?
?
??
? ?[python]?
? ?view plain
? ? copy
? ?
? ?
? ?
??
?
?#!/usr/bin/env python? # coding=utf-8? ? ? class MyList(object):? ? ? ? ? ? # 定義可迭代對象類? ? ? ? def __init__(self, num):? ? ? ? ? self.data = num? ? ? ? ? # 上邊界? ? ? ? def __iter__(self):? ? ? ? ? return MyListIterator(self.data)? # 返回該可迭代對象的迭代器類的實例? ? ? class MyListIterator(object):? ? # 定義迭代器類,其是MyList可迭代對象的迭代器類? ? ? ? def __init__(self, data):? ? ? ? ? self.data = data? ? ? ? ?# 上邊界? ? ? ? ? self.now = 0? ? ? ? ? ? ?# 當前迭代值,初始為0? ? ? ? def __iter__(self):? ? ? ? ? return self? ? ? ? ? ? ? # 返回該對象的迭代器類的實例;因為自己就是迭代器,所以返回self? ? ? ? def next(self):? ? ? ? ? ? ? # 迭代器類必須實現的方法? ? ? ? ? while self.now < self.data:? ? ? ? ? ? ? self.now += 1? ? ? ? ? ? ? return self.now - 1? # 返回當前迭代值? ? ? ? ? raise StopIteration? ? ? # 超出上邊界,拋出異常? ? ? my_list = MyList(5)? ? ? ? ? ? ? # 得到一個可迭代對象? print type(my_list)? ? ? ? ? ? ? # 返回該對象的類型? ? my_list_iter = iter(my_list)? ? ?# 得到該對象的迭代器實例,iter函數在下面會詳細解釋? print type(my_list_iter)? ? ? for i in my_list:? ? ? ? ? ? ? ? # 迭代? ? ? print i??
?
運行結果:
?
?
問題:上面的例子中出現了iter函數,這是什么東西?和__iter__方法有關系嗎?
?
其實該函數與迭代是息息相關的,通過在Python命令行中打印“help(iter)”得知其有以下兩種用法。
?
?
用法一:iter(callable, sentinel)
?
不停的調用callable,直至其的返回值等于sentinel。其中的callable可以是函數,方法或實現了__call__方法的實例。
?
?
用法二:iter(collection)
?
1)用于返回collection對象的迭代器實例,這里的collection我認為表示的是可迭代對象,即該對象必須實現__iter__方法;
事實上iter函數與__iter__方法聯系非常緊密,iter()是直接調用該對象的__iter__(),并把__iter__()的返回結果作為自己的返回值,故該用法常被稱為“創建迭代器”。
?
2)iter函數可以顯示調用,或當執行“for i in obj:”,Python解釋器會在第一次迭代時自動調用iter(obj),之后的迭代會調用迭代器的next方法,for語句會自動處理最后拋出的StopIteration異常。
?
通過上面的例子,相信對可迭代對象與迭代器有了更具體的認識,那么生成器與它們有什么關系呢?下面簡單談一談
?
?
生成器
?
生成器是一種特殊的迭代器,生成器自動實現了“迭代器協議”(即__iter__和next方法),不需要再手動實現兩方法。
生成器在迭代的過程中可以改變當前迭代值,而修改普通迭代器的當前迭代值往往會發生異常,影響程序的執行。
?
看一個生成器的例子:
?
?
??
? ?[python]?
? ?view plain
? ? copy
? ?
? ?
? ?
??
?
?#!/usr/bin/env python? # coding=utf-8? ? ? def myList(num):? ? ? # 定義生成器? ? ? now = 0? ? ? ? ? ?# 當前迭代值,初始為0? ? ? while now < num:? ? ? ? ? val = (yield now)? ? ? ? ? ? ? ? ? ? ? # 返回當前迭代值,并接受可能的send發送值;yield在下面會解釋? ? ? ? ? now = now + 1 if val is None else val? # val為None,迭代值自增1,否則重新設定當前迭代值為val? ? my_list = myList(5)? ?# 得到一個生成器對象? ? print my_list.next()? # 返回當前迭代值? print my_list.next()? ? my_list.send(3)? ? ? ?# 重新設定當前的迭代值? print my_list.next()? ? print dir(my_list)? ? # 返回該對象所擁有的方法名,可以看到__iter__與next在其中??
?
運行結果:
?
?
具有yield關鍵字的函數都是生成器
,yield可以理解為return,返回后面的值給調用者。不同的是return返回后,函數會釋放,而生成器則不會。在直接調用next方法或用for語句進行下一次迭代時,生成器會從yield下一句開始執行,直至遇到下一個yield。
?
#生成器函數,函數里只要有yield關鍵字def gen_func():
? ? yield 1
? ? yield 2
? ? yield 3
?
?
def fib(index):
? ? if index <= 2:
? ? ? ? return 1
? ? else:
? ? ? ? return fib(index-1) + fib(index-2)
?
?
def fib2(index):
? ? re_list = []
? ? n,a,b = 0,0,1
? ? while n<index:
? ? ? ? re_list.append(b)
? ? ? ? a,b = b, a+b
? ? ? ? n += 1
? ? return re_list
?
?
def gen_fib(index):
? ? n,a,b = 0,0,1
? ? while n<index:
? ? ? ? yield b
? ? ? ? a,b = b, a+b
? ? ? ? n += 1
?
?
for data in gen_fib(10):
? ? print (data)
# print (gen_fib(10))
# 斐波拉契 0 1 1 2 3 5 8
#惰性求值, 延遲求值提供了可能
?
?
def func():
? ? return 1
?
?
if __name__ == "__main__":
? ? #生成器對象, python編譯字節碼的時候就產生了,
? ? gen = gen_func()
? ? for value in gen:
? ? ? ? print (value)
? ? # re = func()
? ? # pass