1. 程式人生 > 其它 >python零基礎必讀--可迭代物件、迭代器與生成器

python零基礎必讀--可迭代物件、迭代器與生成器

技術標籤:pythonpython

本文的文字及圖片來源於網路,僅供學習、交流使用,不具有任何商業用途,版權歸原作者所有,如有問題請及時聯絡我們以作處理

想要學習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
同時使用nextfor來遍歷迭代器
先使用兩次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 返回中間結果。生成器是一種特殊的迭代器,生成器自動實現了迭代器協議,即 iternext 方法,不需要再手動實現兩方法。建立生成器例項如下:

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。