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 可迭代特性意味着它不会立马读取所有序列。这就意味着你可以在
非常长的序列中使用它,而不会有太大的开销。