上QQ阅读APP看书,第一时间看更新
4.1.3 实现迭代协议
下面构建一个支持迭代操作的自定义对象,并找到一个能实现迭代协议的简单方法。
截至目前,在一个对象上实现迭代最简单的方式是使用生成器函数。在4.1.2节中,使用Node类表示树型数据结构,实现了以深度优先遍历树形节点的生成器,代码(real_agreement.py)如下:
class Node: def __init__(self, value): self._value = value self._children = [] def __repr__(self): return f'Node({self._value})' def add_child(self, node): self._children.append(node) def __iter__(self): return iter(self._children) def depth_first(self): yield self for c in self: yield from c.depth_first() if __name__ == '__main__': root = Node(0) child_1 = Node(1) child_2 = Node(2) root.add_child(child_1) root.add_child(child_2) child_1.add_child(Node(3)) child_1.add_child(Node(4)) child_2.add_child(Node(5)) for ch in root.depth_first(): print(f'child is: {ch}')
执行py文件,输出结果如下:
child is: Node(0) child is: Node(1) child is: Node(3) child is: Node(4) child is: Node(2) child is: Node(5)
上述代码中,depth_first()方法简单直观。首先返回自己本身并迭代每一个子节点,然后再通过调用子节点的depth_first()方法(使用yield from语句)返回对应元素。
Python的迭代协议要求一个__iter__()方法返回一个特殊的迭代器对象,这个迭代器对象实现了__next__()方法并通过StopIteration异常标识迭代完成。不过,实现过程通常会比较烦琐。以下示例展示了如何使用一个关联迭代器类重新实现depth_first()方法。
class Node2: def __init__(self, value): self._value = value self._children = [] def __repr__(self): return f'Node({self._value})' def add_child(self, node): self._children.append(node) def __iter__(self): return iter(self._children) def depth_first(self): return DepthFirstIterator(self) class DepthFirstIterator(object): def __init__(self, start_node): self._node = start_node self._children_iter = None self._child_iter = None def __iter__(self): return self def __next__(self): # Return myself if just started; create an iterator for children if self._children_iter is None: self._children_iter = iter(self._node) return self._node # If processing a child, return its next item elif self._child_iter: try: next_child = next(self._child_iter) return next_child except StopIteration: self._child_iter = None return next(self) # Advance to the next child and start its iteration else: self._child_iter = next(self._children_iter).depth_first() return next(self)
DepthFirstIterator类和前面使用生成器的版本工作原理类似,但是它写起来很烦琐,因为迭代器必须在迭代处理过程中维护大量的状态信息。在实际应用中,没人愿意写这么晦涩的代码,而是将迭代器定义为一个生成器。