python中的迭代器

深藍小佛發表於2022-03-23

迭代器:iterator
可迭代物件:iterable

迭代器

在本文中,我們將學習迭代器是如何工作的,以及如何使用 __iter__()__next__() 方法構建自己的迭代器。

迭代器(Iterator)是可以迭代的物件,在 Python 中無處不在。它們在 for 迴圈、推導式、生成器等中得到了優雅的實現,但卻隱藏在顯而易見的地方。

Python 中的迭代器只是一個可以迭代的物件。一個每次僅僅返回一個元素的物件(有點像擠牙膏)。從技術上講,Python 迭代器物件必須實現兩個魔法方法:__iter__()__next__()方法,統稱為迭代器協議(iterator protocol)。

如果我們可以從一個物件中得到一個迭代器,那麼這個物件就被稱為可迭代物件(iterable)。Python 中的大多數內建資料結構(容器)都是可迭代物件,比如 list列表、 tuple元組、 str字串等等。iter ()函式(反過來呼叫__iter__()方法)從它們中返回一個迭代器。

遍歷迭代器

我們使用 next() 函式手動遍歷迭代器的所有元素。當我們到達結尾時,如果沒有更多的資料要返回,它將引發 StopIteration異常。

示例:

# 定義一個列表
my_list = [4, 7, 0, 3]

# 使用iter()返回一個迭代器物件
my_iter = iter(my_list)

# 使用next()方法依次遍歷
print(next(my_iter))  # 將列印 4
print(next(my_iter))  # 將列印 7

# next(obj) 和 obj.__next__()效果一樣
print(my_iter.__next__())  # 將列印 0
print(my_iter.__next__())  # 將列印 3

next(my_iter)  # 將會引起 StopIteration 異常

輸出結果:

4
7
0
3
Traceback (most recent call last):
  File "<string>", line 24, in <module>
    next(my_iter)
StopIteration

一種更優雅的自動迭代方式是使用 for 迴圈。這樣一來,我們可以遍歷任何可以返回迭代器的物件,例如列表、字串、檔案等等。

示例:

# 定義一個列表
my_list = [4, 7, 0, 3]

# 使用for迴圈遍歷
for i in my_list:
    print(i)

迭代器中的for迴圈

正如我們在上面的例子中看到的,for迴圈能夠自動遍歷列表。實際上,for 迴圈可以遍歷任何可迭代的物件。讓我們仔細看看 for 迴圈是如何在 Python 中實現的。

# 從可迭代物件中建立一個迭代器物件
iter_obj = iter(iterable)

# 開啟無限迴圈
while True:
    try:
        # 遍歷元素
        element = next(iter_obj)
        # 對元素進行一些操作
        pass
    except StopIteration:
        # 如果引起StopIteration則終止迴圈
        break

由此可見,for 迴圈在內部通過對可迭代物件(iterable)呼叫 iter()方法,來建立出一個迭代器(iterator)物件 iter_obj

笑不活的是,這個 for迴圈實際上竟是一個無限 while迴圈......意不意外,驚不驚喜?。

自定義迭代器

在 Python 中,從零開始構建迭代器很容易,我們只需要實現 __iter__()__next__() 方法。

  • __iter__()方法返回迭代器物件本身. 如果需要,可以執行一些初始化。

  • __next__()方法必須返回序列中的下一項。在到達結尾時,以及在隨後的呼叫中,它必須引發StopIteration 異常。

下面,我們展示一個例子,它將給出每次迭代中2的下一次冪。其中冪指數從零開始到使用者設定的數字。

class PowTwo:
    """2的迭代器指數類"""

    def __init__(self, max=0):
        self.max = max

    def __iter__(self):
        self.n = 0
        return self

    def __next__(self):
        if self.n <= self.max:
            result = 2 ** self.n
            self.n += 1
            return result
        else:
            raise StopIteration


# 建立可迭代物件
numbers = PowTwo(3)

# 獲得一個迭代器
i = iter(numbers)

# 獲取下一個元素
print(next(i))
print(next(i))
print(next(i))
print(next(i))

執行後,輸出結果:

1
2
4
8

我們還可以使用 for 迴圈迭代迭代器類。

for i in PowTwo(5):
    print(i)

執行後輸出結果:

1
2
4
8
16
32

無限迭代器

迭代器物件中的項不必用盡。可以有無限迭代器(它永遠不會結束)。在處理這樣的迭代器時,我們必須小心。

下面是演示無限迭代器的一個簡單示例。

內建函式 iter()還有一種用法是:

iter(callable, sentinel) -> iterator

也就是說,它在呼叫時可以接收兩個引數 ,其中第一個引數必須是可呼叫物件(函式) ,第二個引數必須是哨兵。迭代器呼叫這個函式,直到返回的值等於哨兵。

我們知道python中的 int()函式預設總是返回0。因此,將它作為 iter(int,1)傳遞將返回一個呼叫 int()的迭代器,直到返回的值等於1。這種情況從來沒有發生,我們得到了一個無限迭代器。

不僅如此,我們還可以構建自己的無限迭代器。

class InfIter:
    """一個用來返回所有的奇數的無限迭代器類"""

    def __iter__(self):
        self.num = 1
        return self

    def __next__(self):
        num = self.num
        self.num += 2
        return num

執行後輸出結果:

1
3
5
...

其中...表示後續輸出無窮無盡。

因此,在遍歷這些型別的無限迭代器時,要注意包含終止條件。

其實,Python中還有一種更簡單的建立迭代器的方法,就是使用生成器 generator

---END

相關文章