1. 程式人生 > >Python的迭代器(iterator)和生成器(generator)

Python的迭代器(iterator)和生成器(generator)

前言:
迭代的意思是重複做一些事很多次-就像迴圈中那樣,for迴圈中對序列和字典進行迭代,但是實際上也能對其他的物件進行迭代:實現__iter__方法的物件。

迭代器

__iter__方法返回一個迭代器。所謂的迭代器就是具有next方法(方法不需要引數)的物件。在呼叫next方法時,迭代器會返回它的下一個值。如果next方法被呼叫,迭代器沒有值可以返回,就會引發一個StopIteration異常

  • 迭代器的優點:
    為什麼不使用列表?如果有一個接一個地計算值得函式,那麼在使用時可能是計算一個值時獲取一個值-而不是通過列表一次性獲取所有的值。如果有很多值,列表就會佔用很多的記憶體,使用迭代器就很優雅。
  • 例子:
    一個實現了__iter__方法的物件是可以迭代的,一個實現了next方法的物件則是迭代器。
class Fibs:
def __init__(self):
	self.a = 0
	self.b = 1
def next(self):
	self.a,self.b = self.b,self.a
	return self.a
def __iter__(self):
	return self
  • 迭代器得到列表
    可以將迭代器轉換為列表,使用list(Iterator)方法即可得到迭代的列表。

生成器

解決兩層的巢狀資料結構,比如:列表的列表,a = [[1,2],[3,4],[5,6]]。按順序得到每一個數字,則需要兩個for迭代來完成。
這就是生成器的一個簡單的例子,解決巢狀問題

  • 遞迴生成器
    如果要處理任意層巢狀該怎麼辦?例如可能要用樹形結構表示。所以把解決方案變得更靈活,在不知道會遇到的具體層數的時候,我們可以求助於遞迴(recursion)。
def flatten(nested):
	try:
		for sublist in nested:
			for elemnet in flatten(sublist):
				yield element
	except TypeError:
		yield nested		
  • 通用生成器
    生成器是一個包含yield關鍵字的函式。當它被呼叫時,在函式中的程式碼不會被執行,而會返回一個迭代器。每次請求一個值,就會執行生成器中的程式碼,直到遇到一個yield或者return語句。
    生成器由兩部分組成:生成器的函式和生成器的迭代器。生成器的迭代器是函式的返回部分,函式是用def定義的。
  • 生成器方法
    生成器單位新屬性是在開始執行後為生成器提供值得能力
    send,throw,close方法
    外部作用域訪問生成器的send方法,給生成器傳送一個值,然後yield輸出出來。
    throw丟擲異常
    close停止生成器
  • 作用,有next方法,也可以用for來迭代生成數值,一次生成一個,可以用for 迴圈迭代,用list來序列化

例子:

def flatten(nested):
	try:
		for sublist in nested:
			for element in flatten(sublist):
				yield element
	except TypeError:
		yield nested
a = [[[1,214515,12,45,5],5,5,6],[14,5,45,5],[14,5,[454,4,5,[4,54,4545,[4,54,54,12]]]]]
b = flatten(a)
b.__next__()
c =list(b)
print(c)
for i in b:
    print(i)

out:[214515, 12, 45, 5, 5, 5, 6, 14, 5, 45, 5, 14, 5, 454, 4, 5, 4, 54, 4545, 4, 54, 54, 12]
上述方法中,b就是一個生成器

參考:
通過列表生成式,我們可以直接建立一個列表。但是,受到記憶體限制,列表容量肯定是有限的。而且,建立一個包含100萬個元素的列表,不僅佔用很大的儲存空間,如果我們僅僅需要訪問前面幾個元素,那後面絕大多數元素佔用的空間都白白浪費了。

所以,如果列表元素可以按照某種演算法推算出來,那我們是否可以在迴圈的過程中不斷推算出後續的元素呢?這樣就不必建立完整的list,從而節省大量的空間。在Python中,這種一邊迴圈一邊計算的機制,稱為生成器(Generator)。

要建立一個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>