Python自定義迭代器的實(shí)現(xiàn)方法
引言
自定義迭代器的核心是嚴(yán)格遵守 Python 迭代器協(xié)議。只要一個(gè)類實(shí)現(xiàn)了__iter__()和__next__()這兩個(gè)特殊方法,它的實(shí)例就是一個(gè)合法的迭代器。
一、迭代器協(xié)議的核心要求
任何自定義迭代器都必須滿足以下兩個(gè)條件:
__iter__(self):必須返回迭代器對(duì)象本身(return self)。這是為了讓迭代器可以直接用在for循環(huán)等接受可迭代對(duì)象的地方。__next__(self):- 返回序列中的下一個(gè)元素
- 更新內(nèi)部狀態(tài),指向下一個(gè)元素
- 當(dāng)沒(méi)有更多元素時(shí),必須拋出
StopIteration異常
二、第一個(gè)自定義迭代器:生成指定范圍的偶數(shù)
我們從最簡(jiǎn)單的例子開(kāi)始,實(shí)現(xiàn)一個(gè)能生成從start到end之間所有偶數(shù)的迭代器:
class EvenIterator:
def __init__(self, start, end):
# 初始化迭代器的狀態(tài)
self.current = start if start % 2 == 0 else start + 1 # 確保從偶數(shù)開(kāi)始
self.end = end
def __iter__(self):
# 迭代器必須返回自己
return self
def __next__(self):
if self.current > self.end:
# 沒(méi)有更多元素,拋出異常終止迭代
raise StopIteration
# 保存當(dāng)前值
result = self.current
# 更新?tīng)顟B(tài),指向下一個(gè)偶數(shù)
self.current += 2
# 返回當(dāng)前值
return result
使用這個(gè)迭代器
# 創(chuàng)建迭代器實(shí)例
even_iter = EvenIterator(1, 10)
# 1. 使用next()手動(dòng)遍歷
print(next(even_iter)) # 輸出:2
print(next(even_iter)) # 輸出:4
print(next(even_iter)) # 輸出:6
# 2. 直接用for循環(huán)遍歷(自動(dòng)處理next()和StopIteration)
for num in EvenIterator(1, 10):
# end控制每個(gè)輸出的數(shù)用空格隔開(kāi),默認(rèn)是換行‘\n'
print(num, end=' ') # 輸出:2 4 6 8 10
三、深入理解:迭代器的工作原理
1. 為什么__iter__()必須返回 self?
因?yàn)?Python 中所有接受 "可迭代對(duì)象" 的地方(for循環(huán)、list()、sum()等),都會(huì)先調(diào)用iter(對(duì)象)獲取迭代器。
如果__iter__()返回的不是 self,那么for循環(huán)拿到的就是另一個(gè)對(duì)象,而不是我們的迭代器本身,迭代邏輯就會(huì)失效。
2. 迭代器的 "一次性" 特性
迭代器的狀態(tài)是不可逆的,一旦遍歷到末尾,就無(wú)法再?gòu)念^開(kāi)始:
even_iter = EvenIterator(1, 10) # 第一次遍歷:正常輸出 print(list(even_iter)) # 輸出:[2, 4, 6, 8, 10] # 第二次遍歷:空列表! print(list(even_iter)) # 輸出:[]
這是因?yàn)榈谝淮伪闅v后,self.current已經(jīng)變成了 12,再調(diào)用next()會(huì)直接拋出StopIteration。
3. 如何實(shí)現(xiàn)可多次遍歷的 "可迭代對(duì)象"?
如果需要多次遍歷,應(yīng)該將可迭代對(duì)象和迭代器分開(kāi)實(shí)現(xiàn):
- 可迭代對(duì)象:實(shí)現(xiàn)
__iter__(),每次返回一個(gè)新的迭代器實(shí)例 - 迭代器:實(shí)現(xiàn)
__iter__()和__next__()
# 可迭代對(duì)象(可以多次遍歷)
class EvenNumbers:
def __init__(self, start, end):
self.start = start
self.end = end
def __iter__(self):
# 每次調(diào)用iter()都返回一個(gè)新的迭代器
return EvenIterator(self.start, self.end)
# 現(xiàn)在可以多次遍歷了
evens = EvenNumbers(1, 10)
print(list(evens)) # 輸出:[2, 4, 6, 8, 10]
print(list(evens)) # 輸出:[2, 4, 6, 8, 10]
四、實(shí)用自定義迭代器示例
示例 1:逆序遍歷列表的迭代器
class ReverseIterator:
def __init__(self, data):
self.data = data
self.index = len(data) # 從最后一個(gè)元素的下一個(gè)位置開(kāi)始
def __iter__(self):
return self
def __next__(self):
if self.index == 0:
raise StopIteration
self.index -= 1
return self.data[self.index]
# 使用
for char in ReverseIterator('hello'):
print(char, end=' ') # 輸出:o l l e h
示例 2:無(wú)限斐波那契數(shù)列迭代器
這是迭代器最強(qiáng)大的應(yīng)用之一 —— 生成無(wú)限序列:
class FibonacciIterator:
def __init__(self):
self.a = 0
self.b = 1
def __iter__(self):
return self
def __next__(self):
result = self.a
self.a, self.b = self.b, self.a + self.b
return result
# 使用:按需生成,永遠(yuǎn)不會(huì)結(jié)束
fib = FibonacciIterator()
for _ in range(15):
print(next(fib), end=' ') # 輸出:0 1 1 2 3 5 8 13 21 34 55 89 144 233 377
示例 3:分批處理數(shù)據(jù)的迭代器
class BatchIterator:
def __init__(self, data, batch_size):
self.data = data
self.batch_size = batch_size
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index >= len(self.data):
raise StopIteration
# 計(jì)算當(dāng)前批次的結(jié)束位置
end = self.index + self.batch_size
# 切片獲取當(dāng)前批次
batch = self.data[self.index:end]
# 更新索引
self.index = end
return batch
# 使用
data = list(range(100))
for batch in BatchIterator(data, 10):
print(f"處理批次:{batch}")
# 在這里處理每一批數(shù)據(jù)
五、更簡(jiǎn)單的方式:生成器(Generator)
手動(dòng)實(shí)現(xiàn)__iter__()和__next__()雖然清晰,但比較繁瑣。Python 提供了生成器,可以用更簡(jiǎn)潔的語(yǔ)法創(chuàng)建迭代器。
生成器使用yield關(guān)鍵字,自動(dòng)實(shí)現(xiàn)了迭代器協(xié)議:
# 生成器函數(shù):等價(jià)于上面的EvenIterator
def even_generator(start, end):
current = start if start % 2 == 0 else start + 1
while current <= end:
yield current # 暫停執(zhí)行,返回當(dāng)前值
current += 2
# 使用方式和迭代器完全一樣
for num in even_generator(1, 10):
print(num, end=' ') # 輸出:2 4 6 8 10
生成器的優(yōu)勢(shì)
- 代碼更簡(jiǎn)潔:不需要定義類和兩個(gè)特殊方法
- 自動(dòng)管理狀態(tài):Python 會(huì)自動(dòng)保存函數(shù)的執(zhí)行狀態(tài)
- 可讀性更高:邏輯更直觀,更容易理解
生成器表達(dá)式
對(duì)于簡(jiǎn)單的迭代邏輯,還可以使用生成器表達(dá)式,這是創(chuàng)建迭代器的最簡(jiǎn)潔方式:
# 生成器表達(dá)式:等價(jià)于even_generator(1, 10) even_gen = (x for x in range(1, 11) if x % 2 == 0) print(list(even_gen)) # 輸出:[2, 4, 6, 8, 10]
六、手動(dòng)實(shí)現(xiàn) vs 生成器:如何選擇?
| 實(shí)現(xiàn)方式 | 優(yōu)點(diǎn) | 缺點(diǎn) | 適用場(chǎng)景 |
|---|---|---|---|
| 手動(dòng)實(shí)現(xiàn)迭代器 | 完全控制迭代邏輯,靈活性最高 | 代碼繁瑣,容易出錯(cuò) | 復(fù)雜的迭代邏輯、需要維護(hù)復(fù)雜狀態(tài) |
| 生成器函數(shù) | 代碼簡(jiǎn)潔,可讀性高,自動(dòng)管理狀態(tài) | 靈活性稍低 | 大多數(shù)場(chǎng)景,尤其是中等復(fù)雜度的迭代邏輯 |
| 生成器表達(dá)式 | 最簡(jiǎn)潔,一行代碼搞定 | 只能實(shí)現(xiàn)簡(jiǎn)單邏輯 | 簡(jiǎn)單的轉(zhuǎn)換、過(guò)濾操作 |
七、自定義迭代器的常見(jiàn)坑
- 忘記在
__next__()中拋出StopIteration:會(huì)導(dǎo)致無(wú)限循環(huán) __iter__()沒(méi)有返回 self:迭代器無(wú)法用在 for 循環(huán)中- 迭代器狀態(tài)沒(méi)有正確更新:會(huì)導(dǎo)致重復(fù)返回同一個(gè)元素或跳過(guò)元素
- 試圖多次遍歷同一個(gè)迭代器:第二次遍歷會(huì)得到空結(jié)果
八、總結(jié):自定義迭代器的步驟
- 定義一個(gè)類
- 在
__init__()方法中初始化迭代器的狀態(tài) - 實(shí)現(xiàn)
__iter__()方法,返回 self - 實(shí)現(xiàn)
__next__()方法:- 檢查是否還有更多元素
- 如果沒(méi)有,拋出
StopIteration - 如果有,計(jì)算并返回當(dāng)前元素
- 更新內(nèi)部狀態(tài),指向下一個(gè)元素
總而言之:自定義迭代器是 Python 迭代機(jī)制的底層核心,通過(guò)遵守迭代器協(xié)議,我們可以實(shí)現(xiàn)高效、靈活的數(shù)據(jù)遍歷。生成器是簡(jiǎn)化版的自定義迭代器,適用于大多數(shù)場(chǎng)景;而手動(dòng)實(shí)現(xiàn)類則提供了最高的靈活性,滿足復(fù)雜的迭代需求。
以上就是Python自定義迭代器的實(shí)現(xiàn)方法的詳細(xì)內(nèi)容,更多關(guān)于Python自定義迭代器實(shí)現(xiàn)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Python使用OCR實(shí)現(xiàn)提取掃描PDF的文本
從 PDF 中提取文本一直是很多人的需求,本文將帶你了解如何借助 Python + OCR 技術(shù),從掃描 PDF 中提取可編輯文本,希望對(duì)大家有所幫助2025-09-09
python十進(jìn)制和二進(jìn)制的轉(zhuǎn)換方法(含浮點(diǎn)數(shù))
這篇文章主要介紹了python十進(jìn)制和二進(jìn)制的轉(zhuǎn)換方法(含浮點(diǎn)數(shù)),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-07-07
Python將字符串轉(zhuǎn)換為小寫字母的幾種常用方法
這篇文章主要介紹了Python中將字符串大寫字母轉(zhuǎn)小寫的四種方法:lower()方法簡(jiǎn)潔高效,手動(dòng)ASCII轉(zhuǎn)換靈活可控,str.translate()性能優(yōu)化適合大數(shù)據(jù)處理,列表推導(dǎo)式實(shí)現(xiàn)代碼簡(jiǎn)潔,總結(jié)方法優(yōu)缺點(diǎn)及適用場(chǎng)景,需要的朋友可以參考下2025-05-05
請(qǐng)不要重復(fù)犯我在學(xué)習(xí)Python和Linux系統(tǒng)上的錯(cuò)誤
本人已經(jīng)在運(yùn)維行業(yè)工作了將近十年,我最早接觸Linux是在大二的樣子,那時(shí)候只追求易懂,所以就選擇了Ubuntu作為學(xué)習(xí)、使用的對(duì)象,它簡(jiǎn)單、易用、好操作、界面絢麗,對(duì)于想接觸Linux的新手來(lái)說(shuō)是非常不錯(cuò)的2016-12-12
Python發(fā)送郵件測(cè)試報(bào)告操作實(shí)例詳解
這篇文章主要介紹了Python發(fā)送郵件測(cè)試報(bào)告操作,結(jié)合實(shí)例形式較為詳細(xì)的分析了Python郵件發(fā)送相關(guān)模塊使用及操作注意事項(xiàng),需要的朋友可以參考下2018-12-12
Pyramid將models.py文件的內(nèi)容分布到多個(gè)文件的方法
默認(rèn)的Pyramid代碼結(jié)構(gòu)中,就只有一個(gè)models.py文件,在實(shí)際項(xiàng)目中,如果需要對(duì)models進(jìn)行分類,放到不同文件下,應(yīng)該怎么辦2013-11-11
Python中pandas庫(kù)sort_values()方法的使用
最后去看了有關(guān)于 sort_values 的文檔,成功解決先把單詞出現(xiàn)頻次由高往低依次排序,再把頻次相同的情況下的單詞按照 MD5 值排序這個(gè)問(wèn)題,下面通過(guò)本文講解下Python中pandas庫(kù)sort_values()方法的使用,感興趣的朋友一起看看吧2023-07-07
Python使用re模塊正則提取字符串中括號(hào)內(nèi)的內(nèi)容示例
這篇文章主要介紹了Python使用re模塊正則提取字符串中括號(hào)內(nèi)的內(nèi)容,結(jié)合實(shí)例形式分析了Python使用re模塊進(jìn)行針對(duì)括號(hào)內(nèi)容的正則匹配操作,并簡(jiǎn)單解釋了相關(guān)修正符與正則語(yǔ)句的用法,需要的朋友可以參考下2018-06-06

