[转载] 【python魔术方法】迭代器(__iter__和__next__)
參考鏈接: Python __iter __()和__next __()| 將對象轉(zhuǎn)換為迭代器
文章目錄
?`__iter__` 和 `__next__`真正的迭代器總結(jié)
?
?
?
python里面有很多的以__開始和結(jié)尾的函數(shù),利用它們可以完成很多復(fù)雜的邏輯代碼,而且提高了代碼的簡潔性,本文主要總結(jié)了迭代器用到的魔術(shù)方法,并且主要以代碼例子進(jìn)行解釋。?
__iter__ 和 __next__?
其實(shí)這里需要引入一個概念,叫迭代器,常見的就是我們在使用for語句的時候,python內(nèi)部其實(shí)是把for后面的對象上使用了內(nèi)建函數(shù)iter,比如:?
a = [1, 2, 3]
for i in a:
? ? do_something()
?
其實(shí)在python內(nèi)部進(jìn)行了類似如下的轉(zhuǎn)換:?
a = [1, 2, 3]
for i in iter(a):
? ? do_something()
?
那么iter返回的是什么呢,就是一個迭代對象,它主要映射到了類里面的__iter__函數(shù),此函數(shù)返回的是一個實(shí)現(xiàn)了__next__的對象。注意理解這句話,比如:?
class B(object):
? ? def __next__(self):
? ? ? ? raise StopIteration
?
class A(object):
? ? def __iter__(self):
? ? ? ? return B()
?
我們可以看見,A這個類實(shí)現(xiàn)了一個__iter__函數(shù),返回的是B()的實(shí)例對象,其中B里面實(shí)現(xiàn)了__next__這個函數(shù)。?
下面引入幾個概念: Iterable: 有迭代能力的對象,一個類,實(shí)現(xiàn)了__iter__,那么就認(rèn)為它有迭代能力,通常此函數(shù)必須返回一個實(shí)現(xiàn)了__next__的對象,如果自己實(shí)現(xiàn)了,你可以返回self,當(dāng)然這個返回值不是必須的; Iterator: 迭代器(當(dāng)然也是Iterable),同時實(shí)現(xiàn)了__iter__和__next__的對象,缺少任何一個都不算是Iterator,比如上面例子中,A()可以是一個Iterable,但是A()和B()都不能算是和Iterator,因?yàn)锳只實(shí)現(xiàn)了__iter__,而B只實(shí)現(xiàn)了__next__()。?
我們可以使用collections里面的類型來進(jìn)行驗(yàn)證:?
class B(object):
? ? def __next__(self):
? ? ? ? raise StopIteration
?
class A(object):
? ? def __iter__(self):
? ? ? ? return B()
?
?
from collections.abc import *
?
a = A()
b = B()
print(isinstance(a, Iterable))
print(isinstance(a, Iterator))
?
print(isinstance(b, Iterable))
print(isinstance(b, Iterator))
?
結(jié)果是:?
True
False
False
False
?
讓我們稍微對B這個類做一點(diǎn)修改:?
class B(object):
? ? def __next__(self):
? ? ? ? raise StopIteration
?
? ? def __iter__(self):
? ? ? ? return None
?
class A(object):
? ? def __iter__(self):
? ? ? ? return B()
?
?
from collections.abc import *
?
a = A()
b = B()
print(isinstance(a, Iterable))
print(isinstance(a, Iterator))
?
print(isinstance(b, Iterable))
print(isinstance(b, Iterator))
?
結(jié)果是:?
True
False
True
True
?
真正的迭代器?
上面只是做了幾個演示,這里具體說明一下: 當(dāng)調(diào)用iter函數(shù)的時候,生成了一個迭代對象,要求__iter__必須返回一個實(shí)現(xiàn)了__next__的對象,我們就可以通過next函數(shù)訪問這個對象的下一個元素了,并且在你不想繼續(xù)有迭代的情況下拋出一個StopIteration的異常(for語句會捕獲這個異常,并且自動結(jié)束for),下面實(shí)現(xiàn)了一個自己的類似range函數(shù)的功能。?
class MyRange(object):
? ? def __init__(self, end):
? ? ? ? self.start = 0
? ? ? ? self.end = end
?
? ? def __iter__(self):
? ? ? ? return self
?
? ? def __next__(self):
? ? ? ? if self.start < self.end:
? ? ? ? ? ? ret = self.start
? ? ? ? ? ? self.start += 1
? ? ? ? ? ? return ret
? ? ? ? else:
? ? ? ? ? ? raise StopIteration
?
from collections.abc import *
?
a = MyRange(5)
print(isinstance(a, Iterable))
print(isinstance(a, Iterator))
?
for i in a:
? ? print(i)
?
結(jié)果是:?
True
True
0
1
2
3
4
?
接下來我們使用next函數(shù)模擬一次:?
class MyRange(object):
? ? def __init__(self, end):
? ? ? ? self.start = 0
? ? ? ? self.end = end
?
? ? def __iter__(self):
? ? ? ? return self
?
? ? def __next__(self):
? ? ? ? if self.start < self.end:
? ? ? ? ? ? ret = self.start
? ? ? ? ? ? self.start += 1
? ? ? ? ? ? return ret
? ? ? ? else:
? ? ? ? ? ? raise StopIteration
?
a = MyRange(5)
print(next(a))
print(next(a))
print(next(a))
print(next(a))
print(next(a))
print(next(a)) # 其實(shí)到這里已經(jīng)完成了,我們在運(yùn)行一次查看異常
?
可以看見一個很明顯的好處是,每次產(chǎn)生的數(shù)據(jù),是產(chǎn)生一個用一個,什么意思呢,比如我要遍歷[0, 1, 2, 3.....]一直到10億,如果使用列表的方式,那么是會全部載入內(nèi)存的,但是如果使用迭代器,可以看見,當(dāng)用到了(也就是在調(diào)用了next)才會產(chǎn)生對應(yīng)的數(shù)字,這樣就可以節(jié)約內(nèi)存了,這是一種懶惰的加載方式。?
總結(jié)?
可以使用collection.abs里面的Iterator和Iterable配合isinstance函數(shù)來判斷一個對象是否是可迭代的,是否是迭代器對象iter實(shí)際是映射到了__iter__函數(shù)只要實(shí)現(xiàn)了__iter__的對象就是可迭代對象(Iterable),正常情況下,應(yīng)該返回一個實(shí)現(xiàn)了__next__的對象(雖然這個要求不強(qiáng)制),如果自己實(shí)現(xiàn)了__next__,當(dāng)然也可以返回自己同時實(shí)現(xiàn)了__iter__和__next__的是迭代器(Iterator),當(dāng)然也是一個可迭代對象了,其中__next__應(yīng)該在迭代完成后,拋出一個StopIteration異常for語句會自動處理這個StopIteration異常以便結(jié)束for循環(huán)?
生成器相關(guān)的文檔已經(jīng)在這里。
總結(jié)
以上是生活随笔為你收集整理的[转载] 【python魔术方法】迭代器(__iter__和__next__)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 在Python中模拟do-while循环
- 下一篇: 您如何从Python的stdin中读取信