python设计模式–迭代器模式(Iterator Pattern)

Python 设计模式——迭代器模式(Iterator Pattern)详解

本文面向零基础新手,从概念到“遍历与集合解耦”,从 Python 的 for 与协议到自定义迭代器,全方位讲解迭代器模式。


一、什么是迭代器模式?

1.1 通俗理解

迭代器模式是一种行为型设计模式,核心思想是:

把“遍历集合元素”这件事从集合本身拆出来,交给一个独立的“迭代器”对象去做;客户通过迭代器按顺序访问元素,而不必关心集合内部是怎么存的(数组、链表、树、散列表等)。这样集合负责“装数据”,迭代器负责“按某种顺序一个一个拿出来”,两者解耦,且可以针对同一集合提供多种遍历方式。

可以这样类比:

  • 不用迭代器:你要遍历一本书的每一页,就得知道书是“第几页到第几页”、怎么翻;若书换成“按章节存的电子书”,你又得写另一套“按章节取”的代码。遍历逻辑和“书怎么存”绑在一起。
  • 用迭代器:书(集合)提供一个“迭代器”:你只问迭代器“还有下一页吗?”“给我下一页”;迭代器内部知道书的结构,按顺序一页一页给你。你不需要知道书是数组还是链表,只要会问“下一个”就行。

所以,迭代器模式解决的是:统一、安全地遍历各种集合,而不暴露集合内部结构,并支持多种遍历方式(正序、逆序、过滤等)。

1.2 为什么需要迭代器模式?

场景一:集合内部结构不想暴露

客户只关心“按顺序拿元素”,不关心集合是数组、链表还是树。若直接暴露内部结构(如 list 下标、树节点),会破坏封装且难以更换实现。迭代器对外只提供“下一个元素”“是否还有”等接口,内部结构被隐藏。

场景二:统一遍历方式

列表、字典、树、自定义容器,遍历方式各不相同;若每种都写一套 for 逻辑,客户代码会充满 if-else。用迭代器后,客户只写“用迭代器取下一个”,所有集合只要提供迭代器,就可以用同一套遍历代码(如 for 循环)。

场景三:多种遍历方式

同一集合有时要“正序”,有时要“逆序”,有时要“只遍历偶数下标”。若遍历逻辑写在集合里,会塞满各种方法;拆成多种迭代器(正序迭代器、逆序迭代器、过滤迭代器),集合只负责创建对应的迭代器,更清晰。

1.3 适用场景(什么时候用?)

场景 说明
统一遍历多种集合 列表、树、图、自定义容器,用同一套“取下一个”的接口。
隐藏集合内部结构 不暴露下标、节点指针等,只通过迭代器访问。
支持多种遍历 正序、逆序、按条件过滤等,每种一个迭代器类。

简单记忆:要按顺序访问集合元素且希望不依赖内部实现、可多种遍历时,用迭代器模式。


二、迭代器模式的结构(两个角色)

角色 说明
迭代器(Iterator) 定义“取下一个”“是否还有”等接口;具体实现里按某种顺序访问集合元素。
集合/聚合(Aggregate) 负责存储元素,并提供一个方法(如 create_iterator()__iter__())返回迭代器,让客户通过迭代器遍历。

关系可以理解为:

  • 客户不直接操作集合内部,而是先向集合要一个迭代器,再用迭代器逐个取元素。
  • 迭代器内部会持有对集合的引用(或集合内部数据的副本/视图),在 next() 时按约定顺序返回下一个元素。

三、Python 自带的迭代器协议(重要)

在 Python 里,迭代器模式已经内置到语言中,用迭代器协议实现:

  • 可迭代对象(Iterable):有 __iter__() 方法,返回一个迭代器;例如 list、dict、自定义容器。
  • 迭代器(Iterator):有 __next__() 方法,每次返回下一个元素,没有时抛出 StopIteration;通常也有 __iter__() 并返回 self,这样迭代器本身也可以被 for 使用。
  • for 循环for x in obj 会先调 obj.__iter__() 得到迭代器,再反复调迭代器的 __next__() 直到 StopIteration

因此:让你的类实现 __iter____next__(或只实现 __iter__ 并返回一个带 __next__ 的对象),就实现了迭代器模式,并可直接用于 for。


四、示例一:自定义集合与迭代器(显式写法)

需求:一个简单集合类 MyList,内部用 list 存数据;提供一个迭代器,按顺序遍历。先用“显式”的迭代器类写,便于理解角色。

4.1 迭代器接口 + 具体迭代器

from abc import ABC, abstractmethod

class Iterator(ABC):
    @abstractmethod
    def has_next(self) -> bool:
        pass

    @abstractmethod
    def next(self):
        pass

class MyListIterator(Iterator):
    def __init__(self, data: list):
        self._data = data
        self._index = 0

    def has_next(self) -> bool:
        return self._index < len(self._data)

    def next(self):
        if not self.has_next():
            raise StopIteration
        val = self._data[self._index]
        self._index += 1
        return val

4.2 集合:提供创建迭代器的方法

class MyList:
    """集合:内部存数据,对外提供迭代器"""
    def __init__(self):
        self._data = []

    def add(self, x):
        self._data.append(x)

    def create_iterator(self) -> MyListIterator:
        return MyListIterator(self._data)

4.3 客户通过迭代器遍历

coll = MyList()
coll.add(1)
coll.add(2)
coll.add(3)
it = coll.create_iterator()
while it.has_next():
    print(it.next())  # 1, 2, 3

要点:客户只依赖迭代器的 has_nextnext,不依赖 MyList 内部是 list 还是别的结构。


五、示例二:实现 Python 的 iternext(用于 for)

需求:让自定义集合支持 for x in collection,并让迭代器符合 Python 协议。

5.1 集合实现 iter,返回迭代器

class MyList:
    def __init__(self):
        self._data = []

    def add(self, x):
        self._data.append(x)

    def __iter__(self):
        """返回迭代器,用于 for 循环"""
        return _MyListIterator(self._data)

class _MyListIterator:
    """内部迭代器:实现 __next__,供 for 使用"""
    def __init__(self, data: list):
        self._data = data
        self._index = 0

    def __iter__(self):
        return self  # 迭代器自己也实现 __iter__,返回 self

    def __next__(self):
        if self._index >= len(self._data):
            raise StopIteration
        val = self._data[self._index]
        self._index += 1
        return val

5.2 使用 for

coll = MyList()
coll.add("a")
coll.add("b")
coll.add("c")
for x in coll:
    print(x)  # a, b, c

要点for x in coll 会调 coll.__iter__() 得到迭代器,再反复调迭代器的 __next__();没有元素时迭代器抛 StopIteration,for 自动结束。


六、示例三:逆序迭代器(多种遍历方式)

需求:同一集合,有时要正序,有时要逆序;集合提供两种迭代器(或两种方法返回不同迭代器)。

class MyList:
    def __init__(self):
        self._data = []

    def add(self, x):
        self._data.append(x)

    def __iter__(self):
        return iter(self._data)  # 正序:直接用 list 的迭代器

    def reverse_iterator(self):
        """逆序迭代器"""
        return _ReverseIterator(self._data)

class _ReverseIterator:
    def __init__(self, data: list):
        self._data = data
        self._index = len(data) - 1

    def __iter__(self):
        return self

    def __next__(self):
        if self._index < 0:
            raise StopIteration
        val = self._data[self._index]
        self._index -= 1
        return val

# 使用
coll = MyList()
for i in [1, 2, 3]:
    coll.add(i)
for x in coll:
    print(x)       # 1, 2, 3
for x in coll.reverse_iterator():
    print(x)       # 3, 2, 1

要点:集合不暴露内部存储,正序用 for in coll,逆序用 reverse_iterator(),多种遍历方式由不同迭代器提供。


七、示例四:过滤迭代器(只遍历满足条件的元素)

需求:在遍历时只返回满足条件的元素,例如“只返回偶数”。

class FilterIterator:
    """只迭代满足条件的元素"""
    def __init__(self, data: list, condition):
        self._data = data
        self._condition = condition  # 函数:elem -> bool
        self._index = 0

    def __iter__(self):
        return self

    def __next__(self):
        while self._index < len(self._data):
            val = self._data[self._index]
            self._index += 1
            if self._condition(val):
                return val
        raise StopIteration

# 使用
data = [1, 2, 3, 4, 5, 6]
for x in FilterIterator(data, lambda x: x % 2 == 0):
    print(x)  # 2, 4, 6

要点:迭代器内部跳过不满足条件的元素,客户拿到的就是“过滤后的序列”;集合本身不变,多种过滤可由不同 FilterIterator 实现。


八、示例五:迭代器本身也可迭代(iter() 与 next() 内置函数)

Python 内置的 iter(obj) 会调 obj.__iter__()next(it) 会调 it.__next__()。因此:

coll = MyList()
coll.add(10)
coll.add(20)
it = iter(coll)   # 等价于 coll.__iter__()
print(next(it))   # 10
print(next(it))   # 20
# next(it)        # 抛 StopIteration

要点:只要实现了协议,就可以用 iter()next()for,无需自己写 create_iterator()has_next()(Python 习惯用 __next__ + StopIteration 表示“没有下一个”)。


九、迭代器模式 vs 其他

模式 目的 与迭代器的区别
迭代器 统一、顺序地访问集合元素,不暴露内部结构 强调“遍历”“下一个”“集合与遍历解耦”。
组合 树形结构、统一接口、递归操作 组合的遍历常通过迭代器实现(如遍历子节点);迭代器是“怎么遍历”,组合是“结构是什么”。
访问者 对结构中的每个元素执行某种操作 访问者关心“对每个元素做什么”;迭代器关心“按什么顺序取元素”。

简单记忆:迭代器 = 按顺序取元素 + 不暴露集合内部 + 可多种遍历


十、常见问题与注意点

10.1 迭代器用完后能否重置?

一般不做重置;若要再遍历一遍,重新向集合要一个新的迭代器(如再调一次 __iter__())。这样每个迭代器保持自己的“当前位置”,互不干扰。

10.2 遍历时能否修改集合?

若在 for 循环中增删集合元素,可能导致未定义行为或漏掉/重复元素。通常约定:遍历过程中不要修改原集合;若必须,可先复制一份再遍历,或使用“快照”迭代器。

10.3 生成器(generator)是不是迭代器?

是的。用 yield 的函数会返回一个生成器对象,该对象实现了 __iter____next__,所以生成器就是一种迭代器;不需要手写迭代器类也能实现“按需产生下一个值”,是 Python 里非常常用的写法。


十一、小结

  • 迭代器模式:把遍历集合元素从集合中拆出,由迭代器负责“下一个”“是否结束”;客户通过迭代器顺序访问,不依赖集合内部结构,并可支持多种遍历方式
  • 两个角色:迭代器(next/has_next 或 __next__)、集合(提供迭代器,如 __iter__)。
  • Python 协议:实现 __iter__(返回迭代器)和 __next__(返回下一元素,无则抛 StopIteration),即可用 foriter()next()
  • 典型用途:自定义集合的遍历、正序/逆序/过滤等多种遍历、隐藏内部实现。

建议先写一个带 __iter____next__ 的自定义集合与迭代器并用于 for,再试逆序或过滤迭代器,这样对迭代器模式会掌握得比较扎实。

发表评论