Python語言特性-迭代器和生成器
迭代器:
定義:對於list、string、tuple、dict等這些容器物件,使用for迴圈遍歷是很方便的。在後臺for語句對容器物件呼叫iter()函式。iter()是python內建函式。
iter()函式會返回一個定義了next()方法的迭代器物件,它在容器中逐個訪問容器內的元素。next()也是python內建函式。在沒有後續元素時,next()會丟擲一個StopIteration異常,通知for語句迴圈結束。
迭代器是用來幫助我們記錄每次迭代訪問到的位置,當我們對迭代器使用next()函式的時候,迭代器會向我們返回它所記錄位置的下一個位置的資料。實際上,在使用next()函式的時候,呼叫的就是迭代器物件的_next_方法(Python3中是物件的_next_方法,Python2中是物件的next()方法)。所以,我們要想構造一個迭代器,就要實現它的_next_方法。但這還不夠,python要求迭代器本身也是可迭代的,所以我們還要為迭代器實現_iter_方法,而_iter_方法要返回一個迭代器,迭代器自身正是一個迭代器,所以迭代器的_iter_方法返回自身self即可。
其中迭代器協議是指:物件需要提供next()方法,它要麼返回迭代中的下一項,要麼就引起一個異常,終止迭代。
可迭代物件實現了迭代器協議物件。像list, dict, tuple都是可迭代物件,可以通過內建函式iter() ,把這些都變成iterable(可迭代物件)。
for 迴圈的本質就是先通過iter()函式獲取可迭代物件Iterable的迭代器,然後對回去到的迭代器不斷呼叫next()方法來獲取下一個值並將其賦值給item,當遇到StopIteration的異常後迴圈結束。
Python中一個實現了_iter_方法和_next_方法的類物件,就是迭代器。
下面我們看一個計算斐波那契數列的例子:
class Fib(object): def __init__(self, max): super(Fib, self).__init__() self.max = max def __iter__(self): self.a = 0 self.b = 1 return self def __next__(self): fib = self.a if fib > self.max: raise StopIteration self.a, self.b = self.b, self.a + self.b return fib # 定義一個mian函式,迴圈遍歷 def main(): fib = Fib(10000) for i in fib: print(i) if __name__ == '__main__': main()
解釋說明
在本類的實現中,定義了一個_iter_(self)方法,這個方法是在for迴圈遍歷時被iter()呼叫,返回一個迭代器。因為在遍歷的時候,是直接呼叫的python內建函式iter(),由iter()通過呼叫_iter_(self)獲得物件的迭代器。有了迭代器,就可以逐個遍歷元素了。而逐個遍歷的時候,也是使用內建的next()函式通過呼叫物件_next_(self)方法對迭代器物件進行遍歷。所以要實現_iter_(self)和_next_(self)這兩個方法。
總的來說就是在迴圈遍歷自定義容器物件時,會使用python內建函式iter()呼叫遍歷物件__iter__(self)獲得一個迭代器,之後再迴圈對這個迭代器使用next()呼叫迭代器物件的__next__(self).
iter(self)只會被呼叫一次,而__next__(self)會被呼叫N次,直到出現異常。
生成器
生成器是一類特殊的迭代器,在迴圈遍歷的時候會在需要的時候產生結果,即延遲操作。 生成器值遍歷一次。
可以大概把生成器分為兩類:
第一類:
生成器函式:即使用def定義函式,使用yield語句返回結果。yield語句一次返回一個結果,在每個結果中間,掛起函式的狀態,一遍下次從它離開的地方繼續執行。
下面我們繼續用斐波那契數列來舉例:
def Fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
return ' 請新增資料'
# 呼叫方法,生成出10個數來
f=Fib(10)
# 使用一個迴圈捕獲最後return 返回的值,儲存在異常StopIteration的value中
while True:
try:
x=next(f)
print("f:",x)
except StopIteration as e:
print("生成器最後的返回值是:",e.value)
第二類:
生成器表示式:類似於列表推導式,把[]換成()就可以了。 但是生成器表示式是按需產生一個生成器結果物件,想要拿到每一個元素,就需要迴圈遍歷一次。
如下:
# 一個列表
xiaoke=[2,3,4,5]
# 生成器generator,類似於list,但是是把[]改為()
gen=(a for a in xiaoke)
for i in gen:
print(i)
使用生成器的主要目的就是為了提高效率。用生成器表示式取代列表推導式可以同時節省CPU的使用和記憶體的佔用。
# python內建的一些函式,可以識別這是生成器表示式,外面有一對小括號,就是生成器
ret1=sum(a for a in range(10))
print(ret1)
# 列表推導式
ret2=sum([a for a in range(10)])
print(ret2)