【Python魔術(shù)方法】迭代器(__iter__和__next__)

python里面有很多的以__開(kāi)始和結(jié)尾的函數(shù),利用它們可以完成很多復(fù)雜的邏輯代碼,而且提高了代碼的簡(jiǎn)潔性,本文主要總結(jié)了迭代器用到的魔術(shù)方法,并且主要以代碼例子進(jìn)行解釋。

__iter____next__

其實(shí)這里需要引入一個(gè)概念,叫迭代器,常見(jiàn)的就是我們?cè)谑褂?code>for語(yǔ)句的時(shí)候,python內(nèi)部其實(shí)是把for后面的對(duì)象上使用了內(nèi)建函數(shù)iter,比如:

a = [1, 2, 3]
for i in a:
    do_something()

其實(shí)在python內(nèi)部進(jìn)行了類(lèi)似如下的轉(zhuǎn)換:

a = [1, 2, 3]
for i in iter(a):
    do_something()

那么iter返回的是什么呢,就是一個(gè)迭代對(duì)象,它主要映射到了類(lèi)里面的__iter__函數(shù),此函數(shù)返回的是一個(gè)實(shí)現(xiàn)了__next__的對(duì)象。注意理解這句話,比如:

class B(object):
    def __next__(self):
        raise StopIteration

class A(object):
    def __iter__(self):
        return B()

我們可以看見(jiàn),A這個(gè)類(lèi)實(shí)現(xiàn)了一個(gè)__iter__函數(shù),返回的是B()的實(shí)例對(duì)象,其中B里面實(shí)現(xiàn)了__next__這個(gè)函數(shù)。

下面引入幾個(gè)概念:
Iterable: 有迭代能力的對(duì)象,一個(gè)類(lèi),實(shí)現(xiàn)了__iter__,那么就認(rèn)為它有迭代能力,通常此函數(shù)必須返回一個(gè)實(shí)現(xiàn)了__next__的對(duì)象,如果自己實(shí)現(xiàn)了,你可以返回self,當(dāng)然這個(gè)返回值不是必須的;
Iterator: 迭代器(當(dāng)然也是Iterable),同時(shí)實(shí)現(xiàn)了__iter____next__的對(duì)象,缺少任何一個(gè)都不算是Iterator,比如上面例子中,A()可以是一個(gè)Iterable,但是A()B()都不能算是和Iterator,因?yàn)?code>A只實(shí)現(xiàn)了__iter__,而B只實(shí)現(xiàn)了__next__()。

我們可以使用collections里面的類(lèi)型來(lái)進(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

讓我們稍微對(duì)B這個(gè)類(lèi)做一點(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

真正的迭代器

上面只是做了幾個(gè)演示,這里具體說(shuō)明一下:
當(dāng)調(diào)用iter函數(shù)的時(shí)候,生成了一個(gè)迭代對(duì)象,要求__iter__必須返回一個(gè)實(shí)現(xiàn)了__next__的對(duì)象,我們就可以通過(guò)next函數(shù)訪問(wèn)這個(gè)對(duì)象的下一個(gè)元素了,并且在你不想繼續(xù)有迭代的情況下拋出一個(gè)StopIteration的異常(for語(yǔ)句會(huì)捕獲這個(gè)異常,并且自動(dòng)結(jié)束for),下面實(shí)現(xiàn)了一個(gè)自己的類(lèi)似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

接下來(lái)我們使用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)完成了,我們?cè)谶\(yùn)行一次查看異常

可以看見(jiàn)一個(gè)很明顯的好處是,每次產(chǎn)生的數(shù)據(jù),是產(chǎn)生一個(gè)用一個(gè),什么意思呢,比如我要遍歷[0, 1, 2, 3.....]一直到10億,如果使用列表的方式,那么是會(huì)全部載入內(nèi)存的,但是如果使用迭代器,可以看見(jiàn),當(dāng)用到了(也就是在調(diào)用了next)才會(huì)產(chǎn)生對(duì)應(yīng)的數(shù)字,這樣就可以節(jié)約內(nèi)存了,這是一種懶惰的加載方式。

總結(jié)

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

生成器相關(guān)的文檔已經(jīng)在這里

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

相關(guān)閱讀更多精彩內(nèi)容

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

友情鏈接更多精彩內(nèi)容