【python3-cookbook】第四章:迭代器与生成器
原文:https://python3-cookbook.readthedocs.io/zh_CN/latest/c04/p03_create_new_iteration_with_generators.html
4.1 手动遍历迭代器
为了手动的遍历可迭代对象,使用 next() 函数并在代码中捕获 StopIteration 异常。StopIteration 用来指示迭代的结尾
# 动读取一个文件中的所有行 def manual_iter(): with open('/etc/passwd') as f: try: while True: line = next(f) print(line, end='') except StopIteration: pass
如果你手动使用上面演示的 next() 函数的话,你还可以通过返回一个指定值来标记结尾,比如 None。
with open('/etc/passwd') as f: while True: line = next(f, None) if line is None: break print(line, end='')
4.2 代理迭代
问题
你构建了一个自定义容器对象,里面包含有列表、元组或其他可迭代对象。 你想直接在你的这个新容器对象上执行迭代操作。
解决方案
实际上你只需要定义一个 __iter__() 方法,将迭代操作代理到容器内部的对象上去。比如:
class Node: def __init__(self, value): self._value = value self._children = [] def __repr__(self): return 'Node({!r})'.format(self._value) def add_child(self, node): self._children.append(node) def __iter__(self): return iter(self._children) # Example if __name__ == '__main__': root = Node(0) child1 = Node(1) child2 = Node(2) root.add_child(child1) root.add_child(child2) # Outputs Node(1), Node(2) for ch in root: print(ch)
在上面代码中, __iter__() 方法只是简单的将迭代请求传递给内部的 _children 属性。
4.3 使用生成器创建新的迭代模式
# 一个生产某个范围内浮点数的生成器 def frange(start, stop, increment): x = start while x < stop: yield x x += increment
yield 的作用就是把一个函数变成一个 generator,带有 yield 的函数不再是一个普通函数,Python 解释器会将其视为一个 generator,调用 fab(5) 不会执行 fab 函数,而是返回一个 iterable 对象!在 for 循环执行时,每次循环都会执行 fab 函数内部的代码,执行到 yield b 时,fab 函数就返回一个迭代值,下次迭代时,代码从 yield b 的下一条语句继续执行,而函数的本地变量看起来和上次中断执行前是完全一样的,于是函数继续执行,直到再次遇到 yield
def _get_number(): b=2 for x in [1,2,3,4,5,6]: yield b b=b+x return b

一个函数中需要有一个 yield 语句即可将其转换为一个生成器。 跟普通函数不同的是,生成器只能用于迭代操作。
4.4 实现迭代器协议
问题
你想构建一个能支持迭代操作的自定义对象,并希望找到一个能实现迭代协议的简单方法。
解决方案
目前为止,在一个对象上实现迭代最简单的方式是使用一个生成器函数。 在4.2小节中,使用Node类来表示树形数据结构。你可能想实现一个以深度优先方式遍历树形节点的生成器。 下面是代码示例:
class Node: def __init__(self, value): self._value = value self._children = [] def __repr__(self): return 'Node({!r})'.format(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() # Example if __name__ == '__main__': root = Node(0) child1 = Node(1) child2 = Node(2) root.add_child(child1) root.add_child(child2) child1.add_child(Node(3)) child1.add_child(Node(4)) child2.add_child(Node(5)) for ch in root.depth_first(): print(ch) # Outputs Node(0), Node(1), Node(3), Node(4), Node(2), Node(5)
在这段代码中,depth_first() 方法简单直观。 它首先返回自己本身并迭代每一个子节点并 通过调用子节点的 depth_first() 方法(使用 yield from 语句)返回对应元素。
4.5 反向迭代
# 反方向迭代一个序列 >>> a = [1, 2, 3, 4] >>> for x in reversed(a): ... print(x)
反向迭代仅仅当对象的大小可预先确定或者对象实现了 __reversed__() 的特殊方法时才能生效。 如果两者都不符合,那你必须先将对象转换为一个列表才行。
要注意的是如果可迭代对象元素很多的话,将其预先转换为一个列表要消耗大量的内存。
可以通过在自定义类上实现 __reversed__() 方法来实现反向迭代

浙公网安备 33010602011771号