天天看點

詳解疊代器的使用 | 手把手教你入門Python之八十

上一篇: 自定義異常 | 手把手教你入門Python之七十九 下一篇: 生成器 | 手把手教你入門Python之八十一 本文來自于千鋒教育在阿裡雲開發者社群學習中心上線課程 《Python入門2020最新大課》 ,主講人姜偉。

疊代器

疊代是通路集合元素的⼀種⽅式。疊代器是⼀個可以記住周遊的位置的對象。疊代器對象從集合的第⼀個元素開始通路,直到所有的元素被通路完結束。疊代器隻能往前不會後退。

可疊代對象

我們已經知道可以對list、tuple、str等類型的資料使⽤for...in...的循環文法從其中依次拿到資料進⾏使⽤,我們把這樣的過程稱為周遊,也叫疊代。

但是,是否所有的資料類型都可以放到for...in...的語句中,然後讓for...in...每次從中取出⼀條資料供我們使⽤,即供我們疊代嗎?

>>> for i in 100:
...    print(i)
...
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable
>>>
# int整型不是iterable,即int整型不是可以疊代的           

我們把可以通過for...in...這類語句疊代讀取⼀條資料供我們使⽤的對象稱之為可疊代對象(Iterable)。

如何判斷⼀個對象是否可以疊代

可以使⽤

isinstance()

判斷⼀個對象是否是 Iterable 對象:

In [50]: from collections import Iterable

In [51]: isinstance([], Iterable)
Out[51]: True

In [52]: isinstance({}, Iterable)
Out[52]: True

In [53]: isinstance('abc', Iterable)
Out[53]: True

In [54]: isinstance(mylist, Iterable)
Out[54]: False

In [55]: isinstance(100, Iterable)
Out[55]: False           

可疊代對象的本質

我們分析對可疊代對象進⾏疊代使⽤的過程,發現每疊代⼀次(即在for...in...中每循環⼀次)都會傳回對象中的下⼀條資料,⼀直向後讀取資料直到疊代了所有資料後結束。那麼,在這個過程中就應該有⼀個“⼈”去記錄每次通路到了第⼏條資料,以便每次疊代都可以傳回下⼀條資料。我們把這個能幫助我們進⾏資料疊代的“⼈”稱為疊代器(Iterator)。

可疊代對象的本質就是可以向我們提供⼀個這樣的中間“⼈”即疊代器幫助我們對其進⾏疊代周遊使⽤。

可疊代對象通過

__iter__

⽅法向我們提供⼀個疊代器,我們在疊代⼀個可疊代對象的時候,實際上就是先擷取該對象提供的⼀個疊代器,然後通過這個疊代器來依次擷取對象中的每⼀個資料。

那麼也就是說,⼀個具備了

__iter__

⽅法的對象,就是⼀個可疊代對象。

from collections.abc import Iterable

class Demo(object):
    def __init__(self, n):
        self.n = n
        self.current = 0

    def __iter__(self):
        pass

demo = Demo(10)
print(isinstance(demo, Iterable)) # True

for d in demo: # 重寫了 __iter__ ⽅法以後,demo就是⼀個⼀個可疊代對象了,可以放在for...in的後⾯
    print(d)

# 此時再使⽤for...in循環周遊,會提示 TypeError: iter() returned non-iterator of type 'N
oneType'
# 這是因為,⼀個可疊代對象如果想要被for...in循環,它必須要有⼀個疊代器           

疊代器Iterator

通過上⾯的分析,我們已經知道,疊代器是⽤來幫助我們記錄每次疊代通路到的位置,當我們對疊代器使⽤

next()

函數的時候,疊代器會向我們傳回它所記錄位置的下⼀個位置的資料。實際上,在使⽤

next()

函數的時候,調⽤的就是疊代器對象的

__next__

⽅法(Python3中是對象的

__next__

⽅法,Python2中是對象的

next()

⽅法)。是以,我們要想構造⼀個疊代器,就要實作它的next⽅法。但這還不夠,python要求疊代器本身也是可疊代的,是以我們還要為疊代器實作

__iter__

⽅法,⽽

__iter__

⽅法要傳回⼀個疊代器,疊代器⾃身正是⼀個疊代器,是以疊代器的

__iter__

⽅法傳回⾃身即可。

⼀個實作了iter⽅法和next⽅法的對象,就是疊代器。

class MyIterator(object):
    def __init__(self, n):
        self.n = n
        self.current = 0

    # ⾃定義疊代器需要重寫__iter__和__next__⽅法
    def __iter__(self):
        return self

    def __next__(self):
        if self.current < self.n:
            value = self.current
            self.current += 1
            return value
        else:
            raise StopIteration

my_it = MyIterator(10)

for i in my_it:   # 疊代器重寫了__iter__⽅法,它本身也是⼀個可疊代對象
    print(i)           

如何判斷⼀個對象是否是疊代器

調⽤⼀個對象的

__iter__

⽅法,或者調⽤iter()内置函數,可以擷取到⼀個可疊代對象的疊代器。

names = ['hello', 'good', 'yes']
print(names.__iter__()) # 調⽤對象的__iter__()⽅法
print(iter(names)) # 調⽤iter()内置函數           

可以使⽤ isinstance() 判斷⼀個對象是否是 Iterator 對象:

from collections.abc import Iterator
names = ['hello', 'good', 'yes']
print(isinstance(iter(names), Iterator))           

for...in...循環的本質

for item in Iterable 循環的本質就是先通過iter()函數擷取可疊代對象Iterable的疊代器,然後對擷取到的疊代器不斷調⽤next()⽅法來擷取下⼀個值并将其指派給item,當遇到StopIteration的異常後循環結束。

疊代器的應⽤場景

我們發現疊代器最核⼼的功能就是可以通過next()函數的調⽤來傳回下⼀個資料值。如果每次傳回的資料值不是在⼀個已有的資料集合中讀取的,⽽是通過程式按照⼀定的規律計算⽣成的,那麼也就意味着可以不⽤再依賴⼀個已有的資料集合,也就是說不⽤再将所有要疊代的資料都⼀次性緩存下來供後續依次讀取,這樣可以節省⼤量的存儲(記憶體)空間。

舉個例⼦,⽐如,數學中有個著名的斐波拉契數列(Fibonacci),數列中第⼀個數為0,第⼆個數為1,其後的每⼀個數都可由前兩個數相加得到:

0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ...

現在我們想要通過for...in...循環來周遊疊代斐波那契數列中的前n個數。那麼這個斐波那契數列我們就可以⽤疊代器來實作,每次疊代都通過數學計算來⽣成下⼀個數。

class FibIterator(object):
    """斐波那契數列疊代器"""
    def __init__(self, n):
        """
        :param n: int, 指明⽣成數列的前n個數
        """
        self.n = n
        # current⽤來儲存目前⽣成到數列中的第⼏個數了
        self.current = 0
        # num1⽤來儲存前前⼀個數,初始值為數列中的第⼀個數0
        self.num1 = 0
        # num2⽤來儲存前⼀個數,初始值為數列中的第⼆個數1
        self.num2 = 1

    def __next__(self):
        """被next()函數調⽤來擷取下⼀個數"""
        if self.current < self.n:
            num = self.num1
            self.num1, self.num2 = self.num2, self.num1+self.num2
            self.current += 1
            return num
        else:
            raise StopIteration

    def __iter__(self):
        """疊代器的__iter__傳回⾃身即可"""
        return self


if __name__ == '__main__':
    fib = FibIterator(10)
    for num in fib:
        print(num, end=" ")           

配套視訊