【Python魔術方法】迭代器(__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函數訪問這個對象的下一個元素了,并且在你不想繼續(xù)有迭代的情況下拋出一個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)才會產生對應的數字,這樣就可以節(jié)約內存了,這是一種懶惰的加載方式。

總結

  1. 可以使用collection.abs里面的IteratorIterable配合isinstance函數來判斷一個對象是否是可迭代的,是否是迭代器對象
  2. iter實際是映射到了__iter__函數
  3. 只要實現了__iter__的對象就是可迭代對象(Iterable),正常情況下,應該返回一個實現了__next__的對象(雖然這個要求不強制),如果自己實現了__next__,當然也可以返回自己
  4. 同時實現了__iter____next__的是迭代器(Iterator),當然也是一個可迭代對象了,其中__next__應該在迭代完成后,拋出一個StopIteration異常
  5. for語句會自動處理這個StopIteration異常以便結束for循環(huán)

生成器相關的文檔已經在這里

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 包(lib)、模塊(module) 在Python中,存在包和模塊兩個常見概念。 模塊:編寫Python代碼的py...
    清清子衿木子水心閱讀 3,897評論 0 27
  • 1 迭代器與for 迭代器(iterator),在許多現代編程語言,如C++,Java等中均有出現。迭代器不是一種...
    櫻雨樓閱讀 549評論 0 1
  • 我把朋友圈設成了僅三天可見。 而后,別人看不到我?guī)兹涨鞍l(fā)的那么多照片和狀態(tài)。只有我自己,一遍一遍打開,一遍一遍確認...
    袁筱魚閱讀 2,339評論 6 11
  • 品味,每天都有新的內容,不同的文化形式。背后是董事長趙寶文先生不懈的努力和用心良苦。一個功成名就的老總本可以馬放南...
    劉現輝民俗畫閱讀 751評論 0 0
  • 寫下這個問題的時候,我思考了很久,很久很久。 一直很崇尚簡單。用簡單的說話方式溝通,用簡單的心做事,用簡單的腳步過...
    NJ吳銘閱讀 316評論 0 1

友情鏈接更多精彩內容