『無為則無心』Python基礎 — 62、Python中自定義迭代器
阿新 • • 發佈:2022-03-03
目錄
1、迭代器物件的建立
迭代器是一種可以被遍歷的物件,並且能夠作用於next()
函式,迭代器物件從集合的第一個元素開始訪問,直到所有的元素被訪問完結束,迭代器只能往後遍歷,不能回溯。不像列表,你隨時可以取後面的資料,也可以返回頭取前面的資料,迭代器通常要實現兩個基本方法next()
和iter()
。
概括的說,一個物件實現了__iter__()
和__next__()
方法,那麼它就是一個迭代器物件。
但是隻實現了__iter__()
方法沒有實現__next__()
方法,就只是一個可迭代物件。
例如:
# 3.6之前的版本是不需要帶.abc的,3.7就會提示需要加.abc from collections.abc import Iterable, Iterator class IterA: def __iter__(self): # 我們這裡返回一個列表 return [1, 2, 3] class IterB: pass iterA = IterA() iterB = IterB() # 可以看到iterA 是一個可迭代物件 # iterB 是一個不可迭代物件 print(isinstance(iterA, Iterable)) # True print(isinstance(iterB, Iterable)) # False # iterA是一個可迭代物件,但並不是一個迭代器物件 # 因為IterA類中並沒有實現next方法 print(isinstance(iterA, Iterator)) # False
我們在IterA類中實現__next__()
方法,IterA
類就變成了一個迭代器物件了。
# 3.6之前的版本是不需要帶.abc的,3.7就會提示需要加.abc from collections.abc import Iterable, Iterator class IterA: def __iter__(self): # 我們這裡返回一個列表 return [1, 2, 3] def __next__(self): pass iterA = IterA() # 可以看到iterA 是一個可迭代物件 # iterB 是一個不可迭代物件 print(isinstance(iterA, Iterable)) # True # iterA物件也是要給迭代器物件 print(isinstance(iterA, Iterator)) # True
2、實際應用案例
""" 1.迭代器的應用場景 1).如果數列的資料規模巨大 2).數列有規律,但是依靠列表推導式描述不出來 2.數學中有個著名的斐波那契數列(Fibonacci), 數列中第⼀個數0,第⼆個數1,其後的每⼀個數都可由前兩個數相加得到: 如下: 0, 1, 1,2, 3, 5,8, 13, 21,34, ... 現在我們想要通過for...in...迴圈來遍歷迭代斐波那契數列中的前n個數。 那麼這個斐波那契數列我們就可以⽤迭代器來實現, 每次迭代都通過數學計算來⽣成下⼀個數。 """ from collections.abc import Iterable, Iterator class FibIterator(object): """ fib數列迭代器 """ # 初始化方法 def __init__(self, count): # 斐波拉契數列中的前兩個數 self.num1 = 0 self.num2 = 1 # 用來儲存迭代的總次數 self.count = count # 用來記錄迭代次數(計數器) self.i = 0 # 實現__iter__表示FibIterator是一個可迭代物件 # 返回物件自己。是一個可迭代物件 def __iter__(self): return self # 實現__next__方法,是FibIterator定義為迭代器物件的重要條件之一 def __next__(self): # 判斷是否迭代結束,如果沒有到達迭代次數,則返回資料 # self.count 需要迭代的次數 # self.i已迭代次數 if self.i < self.count: item = self.num1 # 計算num1, num2的值,方便下次迭代返回 # 這裡運用的是序列的封包與解包,不會的可以看我以前的文章(元組) self.num1, self.num2 = self.num2, self.num1 + self.num2 # 執行一次next方法,計數器+1 self.i += 1 # 返回新獲得的數, # 也就是前兩個數求和的第三個數 return item else: # 到達了迭代次數,丟擲異常 raise StopIteration # 建立一個fib數列迭代器物件 fibIter = FibIterator(15) # fibIter物件是一個迭代器 print(isinstance(fibIter, Iterable)) # True print(isinstance(fibIter, Iterator)) # True # 轉換為列表檢視fib物件內容 # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377] print(list(fibIter)) # 遍歷,可執行 for li in fibIter: print(li)
3、總結:
(1)整理總結
- 物件是否實現了
__iter__
方法,如果實現了,該物件就是一個可迭代物件。 - 一個物件實現了
__iter__()
和__next__()
方法,那麼它就是一個迭代器物件。 - 可以使用
iter()
函式把可迭代物件(Iterable
)變成迭代器物件(Iterator
)。 - 通過
isinstance()
函式,可以判斷一個物件是否是Iterable
物件或者是Iterator
物件。print(isinstance(fibIter, Iterable)) print(isinstance(fibIter, Iterator))
(2)迭代協議
當任何可迭代物件傳入到for
迴圈或其他迭代工具中進行遍歷時,迭代工具都是先通過iter()
函式獲得與可迭代物件對應的迭代器,然後再對迭代器呼叫next()
函式,不斷的依次獲取元素,並在捕捉到StopIteration
異常時,確定完成迭代,這就是完整的迭代過程,這也稱之為“迭代協議”。
(3)為什麼任何Python序列都可迭代?
- 都實現了
__getitem__
方法 - 標準序列也都實現了
__iter__
方法 - 實現了
__getitem__
方法,而且其引數是從0開始的索引,這種物件也可迭代,但它不是一個可迭代物件。
原因是:如果沒有實現__iter__
方法,但實現了__getitem__
方法,__getitem__()
方法可以通過iter()
函式轉成Iterator
,即可以在for
迴圈中使用,按順序(從0開始)獲取元素。from collections.abc import Iterable, Iterator class IterObj: def __init__(self): self.a = [3, 5, 7, 11, 13, 17, 19] def __getitem__(self, i): return self.a[i] # 從建立物件 it = IterObj() print(isinstance(it, Iterable)) # false print(isinstance(it, Iterator)) # false # <__main__.IterObj object at 0x0000000002573AC8> print(it) # # <iterator object at 0x10b231278> print(iter(it)) # 遍歷 for i in it: print(i)
歸納:
- 如果這個可迭代物件要在
for
迴圈中被使用,那麼它就應該能夠被內建的iter()
函式呼叫並轉化成Iterator
物件。- Python的for語法功能非常強大,可以遍歷任何可迭代的物件。
參考: