9.7. itertools — 为高效循环创建迭代器的函数 — Python 文档
9.7. 迭代工具 — 为高效循环创建迭代器的函数
2.3 版中的新功能。
该模块实现了许多受 APL、Haskell 和 SML 构造启发的 iterator 构建块。 每个都以适合 Python 的形式重新编写。
该模块标准化了一组快速、内存高效的核心工具,这些工具可以单独使用或组合使用。 它们共同构成了一个“迭代器代数”,使得在纯 Python 中简洁高效地构建专用工具成为可能。
例如,SML 提供了一个制表工具:tabulate(f)
,它产生一个序列 f(0), f(1), ...
。 在Python中也可以通过将imap()和count()组合成imap(f, count())
来达到同样的效果。
这些工具及其内置工具也与 operator 模块中的高速功能配合良好。 例如,乘法运算符可以映射到两个向量上以形成有效的点积:sum(imap(operator.mul, vector1, vector2))
。
无限迭代器:
迭代器 | 参数 | 结果 | 例子 |
---|---|---|---|
count()
|
开始,[步骤] | 开始,开始+步,开始+2*步,… | count(10) --> 10 11 12 13 14 ...
|
cycle()
|
p | p0, p1, ... plast, p0, p1, … | cycle('ABCD') --> A B C D A B C D ...
|
repeat()
|
元素 [,n] | elem, elem, elem, ... 无休止地或最多 n 次 | repeat(10, 3) --> 10 10 10
|
在最短输入序列上终止的迭代器:
迭代器 | 参数 | 结果 | 例子 |
---|---|---|---|
chain()
|
p, q, … | p0, p1, ... plast, q0, q1, … | chain('ABC', 'DEF') --> A B C D E F
|
compress()
|
数据,选择器 | (d[0] 如果 s[0]), (d[1] 如果 s[1]), … | compress('ABCDEF', [1,0,1,0,1,1]) --> A C E F
|
dropwhile()
|
预测,序列 | seq[n], seq[n+1], 当 pred 失败时开始 | dropwhile(lambda x: x<5, [1,4,6,4,1]) --> 6 4 1
|
groupby()
|
可迭代[, keyfunc] | 按 keyfunc(v) 的值分组的子迭代器 | |
ifilter()
|
预测,序列 | seq 的元素,其中 pred(elem) 为真 | ifilter(lambda x: x%2, range(10)) --> 1 3 5 7 9
|
ifilterfalse()
|
预测,序列 | seq 的元素,其中 pred(elem) 为 false | ifilterfalse(lambda x: x%2, range(10)) --> 0 2 4 6 8
|
islice()
|
seq, [开始,] 停止 [, 步骤] | 来自 seq[start:stop:step] 的元素 | islice('ABCDEFG', 2, None) --> C D E F G
|
imap()
|
函数, p, q, … | func(p0, q0), func(p1, q1), … | imap(pow, (2,3,10), (5,2,3)) --> 32 9 1000
|
starmap()
|
功能,序列 | func(*seq[0]), func(*seq[1]), … | starmap(pow, [(2,5), (3,2), (10,3)]) --> 32 9 1000
|
tee()
|
它,n | it1, it2, ... itn 将一个迭代器拆分为 n | |
takewhile()
|
预测,序列 | seq[0], seq[1], 直到 pred 失败 | takewhile(lambda x: x<5, [1,4,6,4,1]) --> 1 4
|
izip()
|
p, q, … | (p[0], q[0]), (p[1], q[1]), … | izip('ABCD', 'xy') --> Ax By
|
izip_longest()
|
p, q, … | (p[0], q[0]), (p[1], q[1]), … | izip_longest('ABCD', 'xy', fillvalue='-') --> Ax By C- D-
|
组合生成器:
迭代器 | 参数 | 结果 |
---|---|---|
product()
|
p, q, ... [重复=1] | 笛卡尔积,相当于嵌套的 for 循环 |
permutations()
|
p[, r] | r-length 元组,所有可能的排序,没有重复的元素 |
combinations()
|
p, r | r 长度的元组,按排序顺序,没有重复的元素 |
combinations_with_replacement()
|
p, r | r 长度元组,按排序顺序,具有重复元素 |
product('ABCD', repeat=2)
|
AA AB AC AD BA BB BC BD CA CB CC CD DA DB DC DD
| |
permutations('ABCD', 2)
|
AB AC AD BA BC BD CA CB CD DA DB DC
| |
combinations('ABCD', 2)
|
AB AC AD BC BD CD
| |
combinations_with_replacement('ABCD', 2)
|
AA AB AC AD BB BC BD CC CD DD
|
9.7.1. 迭代工具函数
以下模块函数都构造和返回迭代器。 有些提供无限长度的流,因此它们只能由截断流的函数或循环访问。
- itertools.chain(*iterables)
制作一个迭代器,从第一个迭代器返回元素,直到它耗尽,然后继续下一个迭代器,直到所有迭代器都耗尽。 用于将连续序列视为单个序列。 大致相当于:
- classmethod chain.from_iterable(iterable)
chain() 的替代构造函数。 从延迟计算的单个可迭代参数获取链接输入。 大致相当于:
2.6 版中的新功能。
- itertools.combinations(iterable, r)
从输入 iterable 返回元素的 r 长度子序列。
组合按字典排序顺序发出。 因此,如果输入 iterable 已排序,则组合元组将按排序顺序生成。
元素被视为唯一基于它们的位置,而不是它们的值。 因此,如果输入元素是唯一的,则每个组合中都不会出现重复值。
大致相当于:
combinations() 的代码也可以表示为 permutations() 的子序列,在过滤元素未按顺序排序的条目后(根据它们在输入池中的位置) :
0 <= r <= n
时返回的项目数为n! / r! / (n-r)!
,r > n
时为0。2.6 版中的新功能。
- itertools.combinations_with_replacement(iterable, r)
从输入 iterable 返回元素的 r 长度子序列,允许单个元素重复多次。
组合按字典排序顺序发出。 因此,如果输入 iterable 已排序,则组合元组将按排序顺序生成。
元素被视为唯一基于它们的位置,而不是它们的值。 因此,如果输入元素是唯一的,则生成的组合也将是唯一的。
大致相当于:
combinations_with_replacement() 的代码也可以表示为 product() 过滤条目后的子序列,其中元素未按排序顺序(根据它们在输入池中的位置) :
n > 0
时返回的项目数为(n+r-1)! / r! / (n-1)!
。2.7 版中的新功能。
- itertools.compress(data, selectors)
制作一个迭代器,过滤来自 data 的元素,只返回那些在 selectors 中具有相应元素的元素,该元素的计算结果为
True
。 当 data 或 selectors 迭代用完时停止。 大致相当于:2.7 版中的新功能。
- itertools.count(start=0, step=1)
创建一个迭代器,返回以 n 开头的均匀间隔值。 通常用作 imap() 的参数来生成连续的数据点。 此外,与 izip() 一起使用以添加序列号。 相当于:
使用浮点数进行计数时,有时可以通过替换乘法代码来获得更好的精度,例如:
(start + step * i for i in count())
。在 2.7 版更改: 添加了 step 参数并允许非整数参数。
- itertools.cycle(iterable)
使迭代器从可迭代对象返回元素并保存每个元素的副本。 当迭代用完时,从保存的副本中返回元素。 无限重复。 大致相当于:
注意,工具包的这个成员可能需要大量的辅助存储(取决于迭代的长度)。
- itertools.dropwhile(predicate, iterable)
制作一个迭代器,只要谓词为真,它就会从可迭代对象中删除元素; 之后,返回每个元素。 请注意,迭代器在谓词第一次变为假之前不会产生 any 输出,因此它可能需要很长的启动时间。 大致相当于:
- itertools.groupby(iterable[, key])
制作一个迭代器,从 iterable 返回连续的键和组。 key 是计算每个元素的键值的函数。 如果未指定或为
None
,则 key 默认为恒等函数并返回元素不变。 通常,迭代需要已经在同一个键函数上排序。groupby()的操作类似于Unix中的
uniq
过滤器。 每次键函数的值发生变化时,它都会生成一个中断或新组(这就是为什么通常需要使用相同的键函数对数据进行排序的原因)。 该行为与 SQL 的 GROUP BY 不同,后者聚合公共元素而不管它们的输入顺序。返回的组本身是一个迭代器,它与 groupby() 共享底层可迭代对象。 因为源是共享的,当 groupby() 对象前进时,之前的组不再可见。 因此,如果稍后需要该数据,则应将其存储为列表:
groupby() 大致相当于:
2.4 版中的新功能。
- itertools.ifilter(predicate, iterable)
制作一个迭代器,从 iterable 中过滤元素,只返回那些谓词为
True
的元素。 如果 predicate 是None
,则返回为真的项目。 大致相当于:
- itertools.ifilterfalse(predicate, iterable)
制作一个迭代器,从 iterable 中过滤元素,只返回那些谓词为
False
的元素。 如果 predicate 是None
,则返回为假的项。 大致相当于:
- itertools.imap(function, *iterables)
制作一个迭代器,使用来自每个可迭代对象的参数来计算函数。 如果 function 设置为
None
,则 imap() 将参数作为元组返回。 与 map() 类似,但在最短的迭代用尽时停止,而不是为较短的迭代填充None
。 不同的原因是无限迭代器参数通常是 map() 的错误(因为输出已完全评估)但代表了向 imap()[ 提供参数的常见且有用的方式X222X]。 大致相当于:
- itertools.islice(iterable, stop)
itertools.islice(iterable, start, stop[, step]) 制作一个迭代器,从可迭代对象中返回选定的元素。 如果 start 非零,则可迭代对象中的元素将被跳过,直到到达 start 为止。 之后,元素将连续返回,除非 step 设置为高于 1 导致项目被跳过。 如果 stop 是
None
,则迭代继续直到迭代器耗尽,如果有的话; 否则,它在指定位置停止。 与常规切片不同,islice() 不支持 start、stop 或 step 的负值。 可用于从内部结构已扁平化的数据中提取相关字段(例如,多行报告可能会在每三行列出一个名称字段)。 大致相当于:如果 start 是
None
,则迭代从零开始。 如果 step 为None
,则 step 默认为 1。在 2.5 版中更改:接受
None
的默认值 start 和 step。
- itertools.izip(*iterables)
制作一个迭代器,聚合来自每个可迭代对象的元素。 与 zip() 类似,只是它返回一个迭代器而不是一个列表。 用于一次对多个可迭代对象进行锁步迭代。 大致相当于:
2.4 版更改: 当未指定可迭代对象时,返回零长度迭代器而不是引发
TypeError
异常。可迭代对象的从左到右的评估顺序是有保证的。 这使得使用
izip(*[iter(s)]*n)
将数据系列聚类为 n 长度组的习语成为可能。izip() 仅当您不关心较长迭代中的尾随不匹配值时,才应与不等长输入一起使用。 如果这些值很重要,请改用 izip_longest()。
- itertools.izip_longest(*iterables[, fillvalue])
制作一个迭代器,聚合来自每个可迭代对象的元素。 如果可迭代对象的长度不均匀,则用 fillvalue 填充缺失值。 迭代一直持续到最长的迭代用完为止。 大致相当于:
如果可迭代对象之一可能是无限的,那么 izip_longest() 函数应该用限制调用次数的东西包装(例如 islice() 或 takewhile( ))。 如果未指定,fillvalue 默认为
None
。2.6 版中的新功能。
- itertools.permutations(iterable[, r])
返回 iterable 中元素的连续 r 长度排列。
如果未指定 r 或
None
,则 r 默认为 iterable 的长度,并生成所有可能的全长排列.排列以字典排序顺序发出。 因此,如果输入 iterable 已排序,则排列元组将按排序顺序生成。
元素被视为唯一基于它们的位置,而不是它们的值。 因此,如果输入元素是唯一的,则每个排列中都不会出现重复值。
大致相当于:
permutations() 的代码也可以表示为 product() 的子序列,过滤以排除具有重复元素的条目(来自输入池中相同位置的条目):
0 <= r <= n
时返回的项目数为n! / (n-r)!
,r > n
时为0。2.6 版中的新功能。
- itertools.product(*iterables[, repeat])
输入迭代的笛卡尔积。
大致相当于生成器表达式中的嵌套 for 循环。 例如,
product(A, B)
返回与((x,y) for x in A for y in B)
相同。嵌套循环就像里程表一样循环,最右边的元素在每次迭代中前进。 此模式创建字典顺序,以便如果输入的可迭代对象已排序,则产品元组按排序顺序发出。
要计算可迭代对象与自身的乘积,请使用可选的 repeat 关键字参数指定重复次数。 例如,
product(A, repeat=4)
与product(A, A, A, A)
的含义相同。这个函数大致相当于下面的代码,只不过实际的实现不会在内存中建立中间结果:
2.6 版中的新功能。
- itertools.repeat(object[, times])
制作一个迭代器,一遍又一遍地返回 object。 无限期运行,除非指定了 times 参数。 用作 imap() 不变函数参数的参数。 还与 izip() 一起用于在元组记录中创建常量字段。 大致相当于:
repeat 的常见用途是向 imap 或 zip 提供常量值流:
- itertools.starmap(function, iterable)
制作一个迭代器,使用从可迭代对象获得的参数来计算函数。 当参数参数已经从单个可迭代对象分组到元组中时(数据已经“预压缩”),用于代替 imap()。 imap() 和 starmap() 之间的区别与
function(a,b)
和function(*c)
之间的区别相似。 大致相当于:2.6 版更改: 以前,starmap() 要求函数参数为元组。 现在,允许任何迭代。
- itertools.takewhile(predicate, iterable)
制作一个迭代器,只要谓词为真,它就会从可迭代对象中返回元素。 大致相当于:
- itertools.tee(iterable[, n=2])
从单个迭代器返回 n 个独立迭代器。 大致相当于:
一旦 tee() 进行了拆分,则不应在其他任何地方使用原始的 iterable; 否则,iterable 可以在不通知 tee 对象的情况下提前。
tee
迭代器不是线程安全的。 当同时使用由相同的 tee() 调用返回的迭代器时,可能会引发RuntimeError
,即使原始的 iterable 是线程安全的。这个 itertool 可能需要大量的辅助存储(取决于需要存储多少临时数据)。 通常,如果一个迭代器在另一个迭代器启动之前使用了大部分或全部数据,则使用
list()
而不是 tee() 会更快。2.4 版中的新功能。
9.7.2. 食谱
本节展示了使用现有 itertools 作为构建块创建扩展工具集的方法。
扩展工具提供与底层工具集相同的高性能。 一次处理一个元素,而不是一次将整个迭代器全部放入内存中,从而保持了卓越的内存性能。 通过以有助于消除临时变量的功能风格将工具链接在一起,代码量保持较小。 通过优先使用“矢量化”构建块而不是使用 for 循环和 生成器 来保持高速,这会导致解释器开销。
请注意,可以通过将全局查找替换为定义为默认值的局部变量来优化上述许多方法。 例如,dotproduct 配方可以写成: