迭代器:iterator
可迭代物件:iterable
迭代器
在本文中,我們將學習迭代器是如何工作的,以及如何使用 __iter__()
和 __next__()
方法構建自己的迭代器。
迭代器(Iterator
)是可以迭代的物件,在 Python 中無處不在。它們在 for 迴圈、推導式、生成器等中得到了優雅的實現,但卻隱藏在顯而易見的地方。
Python 中的迭代器只是一個可以迭代的物件。一個每次僅僅返回一個元素的物件(有點像擠牙膏)。從技術上講,Python 迭代器物件必須實現兩個魔法方法:__iter__()
和 __next__()
方法,統稱為迭代器協議(iterator protocol)。
如果我們可以從一個物件中得到一個迭代器,那麼這個物件就被稱為可迭代物件(iterable
)。Python 中的大多數內建資料結構(容器)都是可迭代物件,比如 list列表、 tuple元組、 str字串等等。iter ()
函式(反過來呼叫__iter__()
方法)從它們中返回一個迭代器。
遍歷迭代器
我們使用 next()
函式手動遍歷迭代器的所有元素。當我們到達結尾時,如果沒有更多的資料要返回,它將引發 StopIteration
異常。
示例:
# 定義一個列表
my_list = [4, 7, 0, 3]
# 使用iter()返回一個迭代器物件
my_iter = iter(my_list)
# 使用next()方法依次遍歷
print(next(my_iter)) # 將列印 4
print(next(my_iter)) # 將列印 7
# next(obj) 和 obj.__next__()效果一樣
print(my_iter.__next__()) # 將列印 0
print(my_iter.__next__()) # 將列印 3
next(my_iter) # 將會引起 StopIteration 異常
輸出結果:
4
7
0
3
Traceback (most recent call last):
File "<string>", line 24, in <module>
next(my_iter)
StopIteration
一種更優雅的自動迭代方式是使用 for 迴圈。這樣一來,我們可以遍歷任何可以返回迭代器的物件,例如列表、字串、檔案等等。
示例:
# 定義一個列表
my_list = [4, 7, 0, 3]
# 使用for迴圈遍歷
for i in my_list:
print(i)
迭代器中的for迴圈
正如我們在上面的例子中看到的,for迴圈能夠自動遍歷列表。實際上,for 迴圈可以遍歷任何可迭代的物件。讓我們仔細看看 for 迴圈是如何在 Python 中實現的。
# 從可迭代物件中建立一個迭代器物件
iter_obj = iter(iterable)
# 開啟無限迴圈
while True:
try:
# 遍歷元素
element = next(iter_obj)
# 對元素進行一些操作
pass
except StopIteration:
# 如果引起StopIteration則終止迴圈
break
由此可見,for 迴圈在內部通過對可迭代物件(iterable
)呼叫 iter()
方法,來建立出一個迭代器(iterator
)物件 iter_obj
。
笑不活的是,這個 for迴圈實際上竟是一個無限 while迴圈......意不意外,驚不驚喜?。
自定義迭代器
在 Python 中,從零開始構建迭代器很容易,我們只需要實現 __iter__()
和 __next__()
方法。
-
__iter__()
方法返回迭代器物件本身. 如果需要,可以執行一些初始化。 -
__next__()
方法必須返回序列中的下一項。在到達結尾時,以及在隨後的呼叫中,它必須引發StopIteration
異常。
下面,我們展示一個例子,它將給出每次迭代中2的下一次冪。其中冪指數從零開始到使用者設定的數字。
class PowTwo:
"""2的迭代器指數類"""
def __init__(self, max=0):
self.max = max
def __iter__(self):
self.n = 0
return self
def __next__(self):
if self.n <= self.max:
result = 2 ** self.n
self.n += 1
return result
else:
raise StopIteration
# 建立可迭代物件
numbers = PowTwo(3)
# 獲得一個迭代器
i = iter(numbers)
# 獲取下一個元素
print(next(i))
print(next(i))
print(next(i))
print(next(i))
執行後,輸出結果:
1
2
4
8
我們還可以使用 for 迴圈迭代迭代器類。
for i in PowTwo(5):
print(i)
執行後輸出結果:
1
2
4
8
16
32
無限迭代器
迭代器物件中的項不必用盡。可以有無限迭代器(它永遠不會結束)。在處理這樣的迭代器時,我們必須小心。
下面是演示無限迭代器的一個簡單示例。
內建函式 iter()
還有一種用法是:
iter(callable, sentinel) -> iterator
也就是說,它在呼叫時可以接收兩個引數 ,其中第一個引數必須是可呼叫物件(函式) ,第二個引數必須是哨兵。迭代器呼叫這個函式,直到返回的值等於哨兵。
我們知道python中的 int()
函式預設總是返回0。因此,將它作為 iter(int,1)
傳遞將返回一個呼叫 int()的迭代器,直到返回的值等於1。這種情況從來沒有發生,我們得到了一個無限迭代器。
不僅如此,我們還可以構建自己的無限迭代器。
class InfIter:
"""一個用來返回所有的奇數的無限迭代器類"""
def __iter__(self):
self.num = 1
return self
def __next__(self):
num = self.num
self.num += 2
return num
執行後輸出結果:
1
3
5
...
其中...表示後續輸出無窮無盡。
因此,在遍歷這些型別的無限迭代器時,要注意包含終止條件。
其實,Python中還有一種更簡單的建立迭代器的方法,就是使用生成器 generator
。
---END