python零基礎必讀--可迭代物件、迭代器與生成器
本文的文字及圖片來源於網路,僅供學習、交流使用,不具有任何商業用途,版權歸原作者所有,如有問題請及時聯絡我們以作處理
想要學習Python?有問題得不到第一時間解決?來看看這裡,滿足你的需求,資料都已經上傳至檔案中,可以自行下載!還有海量最新2020python學習資料。
點選檢視
迭代器是 Python 最強大的功能之一,可以想像如果有個幾十 GB 的大檔案,你需要編寫程式處理其中的文字資訊,如果一次性全部讀入記憶體,估計機器會直接罷工了,但是借住可迭代物件,可以一次從硬碟讀取一小塊內容到記憶體,處理完後寫回硬碟,不斷迭代,從而節省記憶體,加快處理速度。
首先來解釋這3個概念。
(1)可迭代物件:如果一個物件定擁有 iter 方法,那麼這個物件就是一個可迭代物件。這裡順便說下
for 迴圈的處理過程:在 Python 中我們經常使用 for 迴圈來對某個物件進行遍歷,此時被遍歷的這個物件就是可迭代物件,常見的有列表,元組,字典。for 迴圈開始時自動呼叫可迭代物件的 iter 方法獲取一個迭代器,for 迴圈時自動呼叫迭代器的 next 方法獲取下一個元素,當呼叫可迭代器物件的 next 方法引發 StopIteration 異常時,結束 for 迴圈。
(2)迭代器:如果一個物件定擁有 iter 方法和 next 方法,那麼這個物件就是一個迭代器。
(3)生成器:生成器是一類特殊的迭代器,就是在需要的時候才產生結果,不是立即產生結果。這樣可以同時節省 CPU 和記憶體。有兩類方法實現生成器:
- 生成器函式。使用 def 定義函式,使用 yield 而不是 return 語句返回結果。yield 語句一次返回一個結果,在每個結果中間,掛起函式的狀態,以便下次從它離開的地方繼續執行。
- 生成器表示式。類似於列表推導,只不過是把一對大括號 [] 變換為一對小括號() 。但是,生成器表示式是按需產生一個生成器結果物件,要想拿到每一個元素,就需要迴圈遍歷。
三者之間的關係如下圖所示。
三者之間的關係
可迭代物件包含迭代器、序列、字典;生成器是一種特殊的迭代器。下面分別舉例說明。
### 建立一個迭代器
1 class MyListIterator(object): # 定義迭代器類,其是MyList可迭代物件的迭代器類
2
3 def __init__(self, data):
4 self.data = data # 上邊界
5 self.now = 0 # 當前迭代值,初始為0
6
7 def __iter__(self):
8 return self # 返回該物件的迭代器類的例項;因為自己就是迭代器,所以返回self
9
10 def __next__(self): # 迭代器類必須實現的方法
11 while self.now < self.data:
12 self.now += 1
13 return self.now - 1 # 返回當前迭代值
14 raise StopIteration # 超出上邊界,丟擲異常
類 MyListIterator 實現了 iter 方法和 next 方法,因此它是一個迭代器物件,由於 iter 方法本返的是迭代器(本身),因此它也是可迭代物件。迭代器必然是一個可迭代物件。
下面使用3種方法遍歷迭代器 MyListIterator。
1 my_list = MyListIterator(5) # 得到一個迭代器
2 print("使用for迴圈來遍歷迭代器")
3 for i in my_list:
4 print(i)
5 my_list = MyListIterator(5) # 重新得到一個可迭代物件
6 print("使用next來遍歷迭代器")
7 print(next(my_list))
8 print(next(my_list))
9 print(next(my_list))
10 print(next(my_list))
11 print(next(my_list))
12 my_list = MyListIterator(5) # 重新得到一個可迭代物件
13 print("同時使用next和for來遍歷迭代器")
14 print("先使用兩次next")
15 print(next(my_list))
16 print(next(my_list))
17 print("再使用for,會從第三個元素2開始輸出")
18 for i in my_list:
19 print(i)
輸出結果如下:
使用for迴圈來遍歷迭代器
0
1
2
3
4
使用next來遍歷迭代器
0
1
2
3
4
同時使用next和for來遍歷迭代器
先使用兩次next
0
1
再使用for,會從第三個元素2開始輸出
2
3
4
從結果可以看出,for 迴圈實際上就是呼叫了迭代器的 __next__方法,當捕捉到 MyListIterator 異常時自動結束 for 迴圈
建立一個可迭代物件
1 class MyList(object): # 定義可迭代物件類
2 def __init__(self, num):
3 self.data = num # 上邊界
4 def __iter__(self):
5 return MyListIterator(self.data) # 返回該可迭代物件的迭代器類的例項
上例中物件 MyList 實現了 iter 方法返回了迭代器類的例項,因此它是一個可迭代物件。遍歷操作可使用 for 迴圈,無法使用 next()。for 迴圈實質上還是呼叫 MyListIterator 的 next 方法。
1 my_list = MyList(5) # 得到一個可迭代物件
2 print("使用for迴圈來遍歷可迭代物件my_list")
3 for i in my_list:
4 print(i)
5 my_list = MyList(5) # 得到一個可迭代物件
6 print("使用next來遍歷可迭代物件my_list")
7 print(next(my_list))
8 print(next(my_list))
9 print(next(my_list))
10 print(next(my_list))
11 print(next(my_list))
輸出結果如下:
使用for迴圈來遍歷可迭代物件my_list
0
1
2
3
4
使用next來遍歷可迭代物件my_list
print(next(my_list))
TypeError: 'MyList' object is not an iterator
從執行結果知道可迭代物件如果沒有 __next__方法,則無法通過next()進行遍歷。
建立一個生成器
像定義一般函式一樣,只不過使用 yield 返回中間結果。生成器是一種特殊的迭代器,生成器自動實現了迭代器協議,即 iter 和 next 方法,不需要再手動實現兩方法。建立生成器例項如下:
1 def myList(num): # 定義生成器
2 now = 0 # 當前迭代值,初始為0
3 while now < num:
4 val = (yield now) # 返回當前迭代值,
5 now = now + 1 if val is None else val # val為None,迭代值自增1,否則重新設定當前迭代值為val
遍歷生成器:
1 my_list = myList(5) # 得到一個生成器物件
2 print("for 迴圈遍歷生成器myList")
3 for i in my_list:
4 print(i)
5
6 my_list = myList(5) # 得到一個生成器物件
7 print("next遍歷生成器myList")
8 print(next(my_list)) # 返回當前迭代值值
9 print(next(my_list)) # 返回當前迭代值值
10 print(next(my_list)) # 返回當前迭代值值
11 print(next(my_list)) # 返回當前迭代值值
12 print(next(my_list)) # 返回當前迭代值值
執行結果如下:
for 迴圈遍歷生成器myList
0
1
2
3
4
next遍歷生成器myList
0
1
2
3
4
具有 yield 關鍵字的函式都是生成器,yield 可以理解為 return,返回後面的值給呼叫者。不同的是 return 返回後,函式會釋放,而生成器則不會。在直接呼叫 next 方法或用 for 語句進行下一次迭代時,生成器會從 yield 下一句開始執行,直至遇到下一個 yield。