【無爲則無心&Python基礎】— 74、Python中自定義迭代器

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語法功能非常強大,可以遍歷任何可迭代的對象。

參考:

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