參考鏈接: Python __iter __()和__next __()| 將對象轉換為迭代器
文章目錄
?`__iter__` 和 `__next__`真正的迭代器總結
?
?
?
python里面有很多的以__開始和結尾的函數,利用它們可以完成很多復雜的邏輯代碼,而且提高了代碼的簡潔性,本文主要總結了迭代器用到的魔術方法,并且主要以代碼例子進行解釋。?
__iter__ 和 __next__?
其實這里需要引入一個概念,叫迭代器,常見的就是我們在使用for語句的時候,python內部其實是把for后面的對象上使用了內建函數iter,比如:?
a = [1, 2, 3]
for i in a:
? ? do_something()
?
其實在python內部進行了類似如下的轉換:?
a = [1, 2, 3]
for i in iter(a):
? ? do_something()
?
那么iter返回的是什么呢,就是一個迭代對象,它主要映射到了類里面的__iter__函數,此函數返回的是一個實現了__next__的對象。注意理解這句話,比如:?
class B(object):
? ? def __next__(self):
? ? ? ? raise StopIteration
?
class A(object):
? ? def __iter__(self):
? ? ? ? return B()
?
我們可以看見,A這個類實現了一個__iter__函數,返回的是B()的實例對象,其中B里面實現了__next__這個函數。?
下面引入幾個概念: Iterable: 有迭代能力的對象,一個類,實現了__iter__,那么就認為它有迭代能力,通常此函數必須返回一個實現了__next__的對象,如果自己實現了,你可以返回self,當然這個返回值不是必須的; Iterator: 迭代器(當然也是Iterable),同時實現了__iter__和__next__的對象,缺少任何一個都不算是Iterator,比如上面例子中,A()可以是一個Iterable,但是A()和B()都不能算是和Iterator,因為A只實現了__iter__,而B只實現了__next__()。?
我們可以使用collections里面的類型來進行驗證:?
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))
?
結果是:?
True
False
False
False
?
讓我們稍微對B這個類做一點修改:?
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))
?
結果是:?
True
False
True
True
?
真正的迭代器?
上面只是做了幾個演示,這里具體說明一下: 當調用iter函數的時候,生成了一個迭代對象,要求__iter__必須返回一個實現了__next__的對象,我們就可以通過next函數訪問這個對象的下一個元素了,并且在你不想繼續有迭代的情況下拋出一個StopIteration的異常(for語句會捕獲這個異常,并且自動結束for),下面實現了一個自己的類似range函數的功能。?
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)
?
結果是:?
True
True
0
1
2
3
4
?
接下來我們使用next函數模擬一次:?
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)) # 其實到這里已經完成了,我們在運行一次查看異常
?
可以看見一個很明顯的好處是,每次產生的數據,是產生一個用一個,什么意思呢,比如我要遍歷[0, 1, 2, 3.....]一直到10億,如果使用列表的方式,那么是會全部載入內存的,但是如果使用迭代器,可以看見,當用到了(也就是在調用了next)才會產生對應的數字,這樣就可以節約內存了,這是一種懶惰的加載方式。?
總結?
可以使用collection.abs里面的Iterator和Iterable配合isinstance函數來判斷一個對象是否是可迭代的,是否是迭代器對象iter實際是映射到了__iter__函數只要實現了__iter__的對象就是可迭代對象(Iterable),正常情況下,應該返回一個實現了__next__的對象(雖然這個要求不強制),如果自己實現了__next__,當然也可以返回自己同時實現了__iter__和__next__的是迭代器(Iterator),當然也是一個可迭代對象了,其中__next__應該在迭代完成后,拋出一個StopIteration異常for語句會自動處理這個StopIteration異常以便結束for循環?
生成器相關的文檔已經在這里。