1. 程式人生 > >Python語言特性-迭代器和生成器

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)