你所不知道的Python迭代器

迭代就是循環的意思,也就是對一個集合中的元素進行循環,從而得到每一個元素。對於我們自定義的類,也可以讓其支持迭代,這就是本文要介紹的特殊成員方法__iter__的作用。用該成員方法可以自定義一個Python迭代器

自定義可迭代的類

可能有的讀者會問,爲什麼不使用列表呢?列表可以獲取列表的長度,然後使用變量i對列表索引進行循環,不照樣可以獲取集合的所有元素嗎,還容易理解。沒錯,使用列表的代碼是容易理解,也很好操作,但這是要付出代價的。列表之所以可以用索引來快速定位其中的任何一個元素,是因爲列表是一下子將所有的數據都裝載的內存中了,而且是一塊連續的內存空間。如果數據量比較小還好說,如果數據量很大的話,會非常消耗內存資源。而迭代就不同,迭代是讀取多少元素,就將多少元素裝載到內存中,不讀取就不裝載。這有點像處理XML的兩種方式:DOM和SAX。DOM是一下子將所有的XML數據都裝載到內存中,所以可以快速定位任何一個元素,但代價是消耗內存,而SAX是順序讀取XML文檔,沒讀到的XML文檔內容是不會裝載到內存中的,所以SAX比較節省內存,但只能從前向後順序讀取XML文檔的內容。

如果在一個類中定義__iter__方法,那麼這個類的實例就是一個迭代器。iter__方法需要返回一個迭代器,所以就返回對象本身即可(也就是self)。當對象沒迭代一次時,就會調用迭代器中的另外一個特殊成員方法__next。該方法需要返回當前迭代的結果。下面讓我們先看一個簡單的例子,在這個例子中,通過自定義迭代器對由星號(*)組成的直角三角形的每一行進行迭代,然後通過for循環進行迭代,輸出一定行數的直角三角形。

# 可無限迭代直角三角形的行
class RightTriangle:
def __init__(self):
    # 定義一個變量n,表示當前的行數
        self.n = 1
def __next__(self):       
    # 通過字符串的乘法獲取直角三角形每一行的字符串,每一行字符串的長度是2 * n - 1
        result = '*' * (2 * self.n - 1)
        # 行數加1
        self.n += 1
        return result
    # 該方法必須返回一個迭代器
    def __iter__(self):
        return self
rt = RightTriangle()
# 對迭代器進行迭代
for e in rt:
     # 限制輸出行的長度不能大於20,否則會無限輸出行
    if len(e) > 20:
        break;
    print(e)

程序運行結果如下圖所示。
在這裏插入圖片描述

將迭代器轉換爲列表

儘管迭代器很好用,但仍然不具備某些功能,例如,通過索引獲取某個元素,進行分片操作。這些操作都是列表的專利,所以在很多時候,需要將迭代器轉換爲列表。但有很多迭代器都是無限迭代的,就像上一節中的斐波那契數列的迭代。因此,在將迭代器轉換爲列表時,需要給迭代器能夠迭代的元素限定一個範圍,否則內存就會溢出了。如果要讓迭代器停止迭代,只需要拋出StopIteration異常即可。通過list函數可以直接將迭代器轉換爲列表。

下面的代碼會將斐波那契數列迭代器通過list函數轉換爲列表。斐波那契數列迭代器限制了最大迭代值不能超過500。

# 將迭代器轉換爲列表
class Fibonacci:
    def __init__(self):
        self.a = 0
        self.b = 1
    def __next__(self):
        result = self.a
        self.a, self.b = self.b, self.a + self.b
        # 要想讓迭代停止,需要拋出StopIteration異常
        if result > 500: raise StopIteration
        return result
    def __iter__(self):
        return self
 
fibs1 = Fibonacci()
# 將迭代器轉換爲列表
print(list(fibs1))
fibs2 = Fibonacci()
# 使用for循環對迭代器進行迭代
for fib in fibs2:
    print(fib, end = ' ')

程序運行結果如圖下圖所示。

在這裏插入圖片描述
從上面的代碼可以看出,儘管在__next__方法中,當result大於500時拋出了StopIteration異常,但這個異常是在迭代的過程中由系統處理的,並不會在程序中拋出,所以如果要將無限迭代改成有限迭代,可以在適當的時候拋出StopIteration異常。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章