23. 迭代器

星光映梦發表於2024-10-26

一、什麼是迭代器

  迭代器 指的是迭代取值的工具,迭代是一個重複的過程,每次重複都是基於上一次的結果而繼續的,單純的重複並不是重複。迭代器是用來迭代取值的工具,而涉及到把多個值迴圈取出的資料型別有:列表、字串、元組、字典、集合、開啟的檔案物件等。

  我們可以透過 while 迴圈的方式取值,這種取值方式只適用於有索引的資料型別,如:列表、字串、元組等。

names = ["Sakura","Mikoto","Shana"]

i = 0
while i < len(names):
    print(names[i])
    i += 1

  為了解決基於索引迭代器取值的侷限性,Python 需要提供一種能夠不依賴索引的取值方式,這就是迭代器。在 Python 中,但凡是內建有 __iter__() 方法的物件都稱為 可迭代物件。在 Python 中,列表、字串、元組、字典、集合、開啟的檔案物件等都內建了 __iter__() 方法。呼叫可迭代物件的 __iter__() 方法會將其轉換成迭代器物件。轉換為迭代器物件後,我們可以透過 __next__() 方法獲取迭代器物件的下一個值。

names = ["Sakura", "Mikoto", "Shana"]

iterator = person.__iter__()
print(iterator)
print(type(iterator), end="\n\n")

while True:
    try:
        print(iterator.__next__())
    except StopIteration:
        break
names = ["Sakura", "Mikoto", "Shana"]

iterator = iter(names)
print(iterator)
print(type(iterator), end="\n\n")

while True:
    try:
        print(next(iterator))
    except StopIteration:
        break

如果我們迭代到最後一項,再呼叫 __next__() 方法或 next() 方法會丟擲 StopIteration 異常;

  迭代器物件 是指內建有 __next__() 方法和 __iter__() 方法的物件。呼叫迭代器物件的 __next__() 方法將得到迭代器物件的下一個值。呼叫迭代器物件的 __iter__() 方法將得到迭代器的本身,與沒呼叫該方法一樣。

person = {"name":"Sakura","age":10}

iterator = iter(person)
print(iterator is  iterator.__iter__() is iterator.__iter__().__iter__())

  如果我們想判斷某一個資料型別或物件是否可以迭代,我們可以使用 collections 模組來判斷;

from collections.abc import Iterable, Iterator

data = "Sakura"
print("可迭代物件-字串:", isinstance(data, Iterable))
print("迭代器-字串:", isinstance(data, Iterator))
print()

data = ["Sakukra", "Mikoto", "Shana"]
print("可迭代物件-列表:", isinstance(data, Iterable))
print("迭代器-列表:", isinstance(data, Iterator))
print()

data = ("Sakukra", "Mikoto", "Shana")
print("可迭代物件-元組:", isinstance(data, Iterable))
print("迭代器-元組:", isinstance(data, Iterator))
print()

data = {"Sakukra", "Mikoto", "Shana"}
print("可迭代物件-集合:", isinstance(data, Iterable))
print("迭代器-集合:", isinstance(data, Iterator))
print()

data = {"name": "Sakura", "age": 10}
print("可迭代物件-字典:", isinstance(data, Iterable))
print("迭代器-字典:", isinstance(data, Iterator))
print()
from collections.abc import Iterable, Iterator

class People:
    def __init__(self):
        self.container = []
  
people = People()
print("可迭代物件:", isinstance(people, Iterable))
print("迭代器:", isinstance(people, Iterator))

二、自定義迭代器

  只要在類中定義 __iter__() 方法,那麼這個類建立出來的物件一定是可迭代的。通俗的說:一個具備了 __iter__() 方法的物件,就是一個可迭代物件。

from collections.abc import Iterable, Iterator

class People:
    def __init__(self):
        self.container = []

    def __iter__(self):
        pass
  
people = People()
print(isinstance(people, Iterable))
print("迭代器:", isinstance(people, Iterator))

  如果想實現自定義迭代器的話,除了在類中定義 __iter__() 方法還需要在類中再定義 __next__() 方法,即一個實現了 __iter__() 方法和 __next__() 方法的物件,就是迭代器。

from collections.abc import Iterable, Iterator

class People:
    def __init__(self):
        self.container = []
  
    def add(self, name):
        self.container.append(name)

    def __iter__(self):
        # 這個方法有兩個功能:
        # 1.標記用當前類建立出來的物件是一定是可迭代對下個你
        # 2.當呼叫iter()函式時,這個方法會被自動呼叫,返回的自己指定的那個迭代器
        return MyIterator(self)

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

    def __iter__(self):
        pass

    def __next__(self):
        # 這個方法有兩個功能:
        # 1.標記當前類建立的物件(一定有__iter__()方法)一定是迭代器
        # 2.當呼叫next()函式時,這個方法會自動呼叫,它返回一個資料
        if self.current < len(self.obj.container):
            item = self.obj.container[self.current]
            self.current += 1
            return item
        else:
            raise StopIteration
  
people = People()
print("People-可迭代物件:", isinstance(people, Iterable))
print("People-迭代器:", isinstance(people, Iterator))
print()

iterator = MyIterator(people)
print("MyIterator-可迭代物件:", isinstance(iterator, Iterable))
print("MyIterator-迭代器:", isinstance(iterator, Iterator))
print()

people.add("Sakura")
people.add("Mikoto")
people.add("Shana")

for item in people:
    print(item)

 for 迴圈是一個已經實現的功能,它裡面自帶了 iter()、next() 方法,並且帶有 StopIteration 異常判斷,透過這個異常判斷來決定是否還需要獲取迭代器物件。如果自己規定用 None 來表示資料獲取完畢,但是 for 迴圈的程式碼依然用的是異常處理,所以 for 迴圈會產生死迴圈。

from collections.abc import Iterable, Iterator

class People:
    def __init__(self):
        self.container = []
        self.current = 0
  
    def add(self, name):
        self.container.append(name)

    def __iter__(self):
        # 這個方法有兩個功能:
        # 1.標記用當前類建立出來的物件是一定是可迭代對下個你
        # 2.當呼叫iter()函式時,這個方法會被自動呼叫,返回的自己指定的那個迭代器
        return self
  
    def __next__(self):
        # 這個方法有兩個功能:
        # 1.標記當前類建立的物件(一定有__iter__()方法)一定是迭代器
        # 2.當呼叫next()函式時,這個方法會自動呼叫,它返回一個資料
        if self.current < len(self.container):
            item = self.container[self.current]
            self.current += 1
            return item
        else:
            self.current = 0
            raise StopIteration

people = People()

people.add("Sakura")
people.add("Mikoto")
people.add("Shana")

for item in people:

當對一個可迭代物件呼叫 iter() 方法時,它會自動呼叫這個可迭代物件的 __iter__() 方法,這個方法返回的物件當做迭代器;

當對一個迭代器物件i呼叫 next() 方法時,它會自動呼叫這個迭代器物件的 __next__() 方法,這個方法返回想要的那個資料;

相關文章