『無為則無心』Python基礎 — 62、Python中自定義迭代器

繁華似錦Fighting發表於2022-03-03

1、迭代器物件的建立

迭代器是一種可以被遍歷的物件,並且能夠作用於next()函式,迭代器物件從集合的第一個元素開始訪問,直到所有的元素被訪問完結束,迭代器只能往後遍歷,不能回溯。不像列表,你隨時可以取後面的資料,也可以返回頭取前面的資料,迭代器通常要實現兩個基本方法next()iter()

概括的說,一個物件實現了__iter__()__next__()方法,那麼它就是一個迭代器物件。

但是隻實現了__iter__()方法沒有實現__next__()方法,就只是一個可迭代物件。

例如:

# 3.6之前的版本是不需要帶.abc的,3.7就會提示需要加.abc
from collections.abc import Iterable, Iterator


class IterA:

    def __iter__(self):
        # 我們這裡返回一個列表
        return [1, 2, 3]


class IterB:
    pass


iterA = IterA()
iterB = IterB()

# 可以看到iterA 是一個可迭代物件
# iterB 是一個不可迭代物件
print(isinstance(iterA, Iterable))  # True
print(isinstance(iterB, Iterable))  # False

# iterA是一個可迭代物件,但並不是一個迭代器物件
# 因為IterA類中並沒有實現next方法
print(isinstance(iterA, Iterator))  # False

我們在IterA類中實現__next__()方法,IterA類就變成了一個迭代器物件了。

# 3.6之前的版本是不需要帶.abc的,3.7就會提示需要加.abc
from collections.abc import Iterable, Iterator


class IterA:

    def __iter__(self):
        # 我們這裡返回一個列表
        return [1, 2, 3]

    def __next__(self):
        pass


iterA = IterA()

# 可以看到iterA 是一個可迭代物件
# iterB 是一個不可迭代物件
print(isinstance(iterA, Iterable))  # True


# iterA物件也是要給迭代器物件
print(isinstance(iterA, Iterator))  # True

2、實際應用案例

"""
1.迭代器的應用場景
    1).如果數列的資料規模巨大
    2).數列有規律,但是依靠列表推導式描述不出來
 

2.數學中有個著名的斐波那契數列(Fibonacci),
數列中第⼀個數0,第⼆個數1,其後的每⼀個數都可由前兩個數相加得到:
如下:
0,    1,    1,   2,    3,    5,   8,    13,    21,   34,    ...

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

"""
from collections.abc import Iterable, Iterator

class FibIterator(object):
    """
        fib數列迭代器
    """

    # 初始化方法
    def __init__(self, count):
        # 斐波拉契數列中的前兩個數
        self.num1 = 0
        self.num2 = 1

        # 用來儲存迭代的總次數
        self.count = count
        # 用來記錄迭代次數(計數器)
        self.i = 0

    # 實現__iter__表示FibIterator是一個可迭代物件
    # 返回物件自己。是一個可迭代物件
    def __iter__(self):
        return self

    # 實現__next__方法,是FibIterator定義為迭代器物件的重要條件之一
    def __next__(self):
        # 判斷是否迭代結束,如果沒有到達迭代次數,則返回資料
        # self.count 需要迭代的次數
        # self.i已迭代次數
        if self.i < self.count:

            item = self.num1
            # 計算num1, num2的值,方便下次迭代返回
            # 這裡運用的是序列的封包與解包,不會的可以看我以前的文章(元組)
            self.num1, self.num2 = self.num2, self.num1 + self.num2

            # 執行一次next方法,計數器+1
            self.i += 1
            # 返回新獲得的數,
            # 也就是前兩個數求和的第三個數
            return item
        else:
            # 到達了迭代次數,丟擲異常
            raise StopIteration


# 建立一個fib數列迭代器物件
fibIter = FibIterator(15)

# fibIter物件是一個迭代器
print(isinstance(fibIter, Iterable))  # True
print(isinstance(fibIter, Iterator))  # True

# 轉換為列表檢視fib物件內容
# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377]
print(list(fibIter))

# 遍歷,可執行
for li in fibIter:
    print(li)

3、總結:

(1)整理總結

  • 物件是否實現了__iter__方法,如果實現了,該物件就是一個可迭代物件。
  • 一個物件實現了__iter__()__next__()方法,那麼它就是一個迭代器物件。
  • 可以使用iter()函式把可迭代物件(Iterable)變成迭代器物件(Iterator)。
  • 通過isinstance()函式,可以判斷一個物件是否是Iterable物件或者是Iterator物件。
    print(isinstance(fibIter, Iterable))  
    print(isinstance(fibIter, Iterator)) 
    

(2)迭代協議

當任何可迭代物件傳入到for迴圈或其他迭代工具中進行遍歷時,迭代工具都是先通過iter()函式獲得與可迭代物件對應的迭代器,然後再對迭代器呼叫next()函式,不斷的依次獲取元素,並在捕捉到StopIteration異常時,確定完成迭代,這就是完整的迭代過程,這也稱之為“迭代協議”。

(3)為什麼任何Python序列都可迭代?

  1. 都實現了__getitem__方法
  2. 標準序列也都實現了__iter__方法
  3. 實現了__getitem__方法,而且其引數是從0開始的索引,這種物件也可迭代,但它不是一個可迭代物件。
    原因是:如果沒有實現__iter__方法,但實現了__getitem__方法,__getitem__()方法可以通過iter()函式轉成Iterator,即可以在for迴圈中使用,按順序(從0開始)獲取元素。
    from collections.abc import Iterable, Iterator
    
    
    class IterObj:
    
        def __init__(self):
            self.a = [3, 5, 7, 11, 13, 17, 19]
    
        def __getitem__(self, i):
            return self.a[i]
    
    
    # 從建立物件
    it = IterObj()
    print(isinstance(it, Iterable))  # false
    print(isinstance(it, Iterator))  # false
    # <__main__.IterObj object at 0x0000000002573AC8>
    print(it)
    # # <iterator object at 0x10b231278>
    print(iter(it))
    
    # 遍歷
    for i in it:
        print(i)
    

歸納:

  • 如果這個可迭代物件要在for迴圈中被使用,那麼它就應該能夠被內建的iter()函式呼叫並轉化成Iterator物件。
  • Python的for語法功能非常強大,可以遍歷任何可迭代的物件。

參考:

相關文章