Python CookBook 笔记
第四章迭代器和生成器
4.1 手动遍历迭代器
iter()函数可以将list转换为迭代器,从而使用next()函数获取迭代器结果
4.2实现迭代器协议
Python 的迭代协议要求一个 iter() 方法返回一个特殊的迭代器对象,这个迭代器对象实现了 next() 方法并通过 StopIteration 异常标识迭代的完成
class Node2: 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): return DepthFirstIterator(self) class DepthFirstIterator(object): ''' Depth-first traversal ''' 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: nextchild = next(self._child_iter) return nextchild 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)
4.3迭代器切片
函数itertools.islice()可以对迭代器和生成器做切片操作
a = (x for x in range(5)) from itertools import islice for i in islice(a, 1, 3): print(i) # islice() 会消耗掉传入的迭代器中的数据。必须考虑到迭代器是不可逆的这个事实
4.4跳过可迭代对象的开始部分
函数itertools.dropwhile()可根据规则跳过迭代器的某一部分
from itertools import dropwhile with open('/etc/passwd') as f: for line in dropwhile(lambda line: line.startswith('#'), f): print(line, end='')
如果知道需要跳过某一段的话也可以使用itertools.islice()
4.5排列组合迭代
itertools的permutations, combinations, combinations_with_replacement模块分别处理可迭代序列的每个位置元素不重复排列,不区分排列书序的组合,同元素可以出现在相同位置的排列
4.6序列的索引迭代
内置的enumerate()函数可以在迭代过程中输出位置索引
4.7同时迭代多个序列
内置的zip()函数可以同时迭代多个序列,迭代结果以len最短的为准
如果想把所有序列迭代完成那么需要使用itertools.zip_longest()函数,不符合迭代长度的元素输出结果为None
zip()函数还可以对序列进行打包生成可迭代序列,dict作用下会生成字典,list作用下会生成元组对组成的列表
headers = ['name', 'shares', 'price'] values = ['ACME', 100, 490.1] a = zip(headers, values) print(list(a)) a = zip(headers, values) print(dict(a)) """ [('name', 'ACME'), ('shares', 100), ('price', 490.1)] {'name': 'ACME', 'shares': 100, 'price': 490.1} """ # 同样zip迭代序列使用过一次后就被销毁
4.8合并可迭代序列
itertools.chain()可以合并多个序列生成一个迭代器,不会生成一个全新的序列,这种方法要比两个序列相加高效的多,同时不用考虑两个迭代序列类型是否一致
headers = ['name', 'shares', 'price'] values = ('ACME', 100, 490.1) import itertools for x in itertools.chain(headers, values): print(x)
4.9展开可迭代序列
item = ['Dave', 'Paula', ['Thomas', 'Lewis']] from collections.abc import Iterable def flatten(items, ignore_types=(str, bytes)): for x in items: if isinstance(x, Iterable) and not isinstance(x, ignore_types): yield from flatten(x) else: yield x for x in flatten(item): print(x)
4.10 顺序合并排序后的多个可迭代对象
heapq.merge()可以解决这个问题
a = [1, 2, 3] b = [6, 8, 9] import heapq for i in heapq.merge(a, b): print(i) # heapq.merge()输入的必须是已经排序好的序列
heapq.merge 可迭代特性意味着它不会立马读取所有序列。这就意味着你可以在
非常长的序列中使用它,而不会有太大的开销。