迭代器模式练习

迭代器模式练习——自定义集合、逆序与过滤

按《Python 设计模式——迭代器模式》的建议,通过三道练习巩固:① 自定义集合实现 __iter__/__next__ 并用 for 遍历;② 为同一集合提供逆序迭代器;③ 实现一个过滤迭代器。每步都有完整可运行代码和验证要点。


练习一:自定义集合 + for 遍历(iternext

目的

体会 集合迭代器 的分离:集合只负责存数据并返回迭代器,客户通过 for 遍历时只依赖“取下一个”,不关心内部结构。掌握 Python 的 迭代器协议__iter____next__StopIteration)。

要求

  • 实现一个 MyList 类:内部用 list 存数据,有 add(item) 方法。
  • 实现 MyList__iter__(),返回一个迭代器对象;该迭代器实现 __iter__()__next__(),按顺序逐个返回元素,没有下一个时抛出 StopIteration
  • 客户代码使用 for x in my_list 遍历,并打印每个元素;再用 iter()next() 手动取两次,验证行为。

参考答案

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

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

    def __iter__(self):
        return _MyListIterator(self._data)

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

    def __iter__(self):
        return self

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

# 使用 for 遍历
my_list = MyList()
my_list.add("A")
my_list.add("B")
my_list.add("C")
for x in my_list:
    print(x)   # A, B, C

# 使用 iter() 和 next() 手动取
it = iter(my_list)
print(next(it))  # A
print(next(it))  # B
# 再取会抛 StopIteration(或再 next 两次后 try/except 验证)

验证要点

  • for x in my_list 依次打印 A、B、C
  • iter(my_list) 得到迭代器,next(it) 两次得到 A、B;第三次 next(it) 应抛出 StopIteration(可在 try/except 中验证)。
  • 理解:MyList 不直接实现 __next__,而是由它返回的 _MyListIterator 实现;for 循环会先调 my_list.__iter__() 再反复调迭代器的 __next__()

练习二:同一集合提供逆序迭代器

目的

同一集合可以有不同的“遍历方式”;正序用 __iter__(for in),逆序用单独的方法返回逆序迭代器,体会“多种遍历 = 多种迭代器”。

要求

  • 在练习一的基础上,为 MyList 增加一个方法 reverse_iterator(),返回一个按逆序遍历元素的迭代器(从最后一个到第一个)。
  • 逆序迭代器也实现 __iter____next__,可被 for 使用。
  • 客户代码:先 for 正序打印,再 for 逆序打印,验证顺序相反。

参考答案

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

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

    def __iter__(self):
        return _MyListIterator(self._data)

    def reverse_iterator(self):
        return _ReverseIterator(self._data)

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

    def __iter__(self):
        return self

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

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

# 使用
my_list = MyList()
my_list.add(1)
my_list.add(2)
my_list.add(3)
print("正序:", [x for x in my_list])                    # 正序: [1, 2, 3]
print("逆序:", [x for x in my_list.reverse_iterator()]) # 逆序: [3, 2, 1]

验证要点

  • 正序 for 得到 [1, 2, 3],逆序 for 得到 [3, 2, 1]
  • 集合内部结构(list)没有暴露给客户;客户只通过“迭代器”按顺序拿元素。

练习三:过滤迭代器(只遍历满足条件的元素)

目的

迭代器不仅可以“按顺序”取,还可以“按条件”取;实现一个 FilterIterator,接收一个可迭代对象和一个条件函数,只返回满足条件的元素,体会“多种遍历”中的“过滤遍历”。

要求

  • 实现 FilterIterator:构造函数接收 data(list 或任意可迭代对象)和 condition(一个函数,接收元素返回 bool)。
  • FilterIterator 实现 __iter____next__;在 __next__ 中跳过不满足 condition 的元素,只返回满足条件的元素,没有更多满足条件的元素时抛出 StopIteration
  • 客户代码:对列表 [1, 2, 3, 4, 5, 6] 使用 FilterIterator,条件为“偶数”,用 for 遍历应得到 2, 4, 6

参考答案

class FilterIterator:
    def __init__(self, data, condition):
        """
        data: 可迭代对象(如 list)
        condition: 函数 elem -> bool,只保留使 condition(elem) 为 True 的元素
        """
        self._data = list(data)
        self._condition = condition
        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]
evens = [x for x in FilterIterator(data, lambda x: x % 2 == 0)]
print(evens)  # [2, 4, 6]

验证要点

  • FilterIterator(data, lambda x: x % 2 == 0) 用 for 遍历得到 [2, 4, 6]
  • 可选:再写一个条件 lambda x: x > 3,验证得到 [4, 5, 6];说明同一份 data 可以通过不同迭代器得到不同“视图”。

三步汇总与自检

练习 重点 关键点
集合 + 迭代器协议 __iter__ 返回迭代器,迭代器实现 __next__StopIteration;for 依赖这两者。
多种遍历 正序、逆序由不同迭代器类实现;集合提供不同方法返回不同迭代器。
过滤迭代器 迭代器内部跳过不满足条件的元素,只 yield/return 满足条件的;不修改原集合。

自检问题

  1. for x in obj 时,Python 会先调什么?没有下一个元素时迭代器应该怎么做?
    会先调 obj.__iter__() 得到迭代器,再反复调迭代器的 __next__();没有下一个时应抛出 StopIteration,for 自动结束。

  2. 为什么说“迭代器用完后一般不重置”?
    每个迭代器维护自己的“当前位置”;要再遍历一遍应重新从集合取一个新的迭代器(如再执行一次 __iter__()),而不是重置同一个迭代器。

  3. 过滤迭代器会不会修改原集合?
    不会;它只是在遍历时跳过不满足条件的元素,原集合不变;若要“只保留偶数”的集合,需要新建列表或新集合。

做完以上三道练习,再对照《迭代器模式》文档中的示例,对迭代器模式会掌握得比较扎实。建议每道题先自己写一遍,再对照参考答案和验证要点检查。

发表评论