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語法功能非常強大,可以遍歷任何可迭代的物件。
參考: