Python内置itertools模块详解:无限迭代与惰性计算

周予安
周予安
发布时间:2026-10-12 | 2人浏览过
原创

本文深入解析Python内置itertools模块,重点介绍count、cycle、repeat等无限迭代器的行为机制,以及chain、groupby、imap等高效处理迭代对象的函数。通过对比map与imap、filter与ifilter,阐明惰性计算在内存优化和处理无限序列中的关键作用,帮助开发者编写更高效的Python代码。

Python标准库中的itertools模块提供了一系列用于高效处理迭代对象的函数工具。

首先,我们来了解itertools模块提供的几类“无限”迭代器:

>>> import itertools
>>> natuals = itertools.count(1)
>>> for n in natuals:
...   print n
...
1
2
3
...

由于count()函数会生成一个无限递增的自然数序列,上述代码将不断打印数字,无法自动停止,必须通过Ctrl+C强制中断程序。

cycle()函数会将传入的序列无限次重复输出:

>>> import itertools
>>> cs = itertools.cycle('ABC') # 注意字符串也是序列的一种
>>> for c in cs:
...   print c
...
'A'
'B'
'C'
'A'
'B'
'C'
...

该迭代器同样不会自动终止。

repeat()函数用于无限重复单个元素,若提供第二个参数,则可指定重复次数:

>>> ns = itertools.repeat('A', 10)
>>> for n in ns:
...   print n
...

上述代码将打印10次字符'A'。

无限序列仅在for循环迭代时才会持续生成元素;若仅创建迭代对象,系统不会预先生成所有元素,这在内存中也是不可行的。

尽管无限序列可以无限迭代,但通常我们会借助takewhile()等函数,根据特定条件截取有限序列:

>>> natuals = itertools.count(1)
>>> ns = itertools.takewhile(lambda x: x <= 10, natuals)
>>> for n in ns:
...   print n
...

上述代码将输出从1到10的数字。

itertools模块还提供了其他几个极具实用价值的迭代操作函数: chain()函数

chain()函数可将多个迭代对象串联,合并为一个更大的迭代器:

for c in chain('ABC', 'XYZ'):
  print c
# 迭代效果:'A' 'B' 'C' 'X' 'Y' 'Z'

groupby()函数

groupby()函数用于将迭代器中相邻的重复元素分组并提取出来:

>>> for key, group in itertools.groupby('AAABBBCCAAA'):
...   print key, list(group) # 为什么这里要用list()函数呢?
...
A ['A', 'A', 'A']
B ['B', 'B', 'B']
C ['C', 'C']
A ['A', 'A', 'A']

分组规则由传入的函数决定,只要该函数对两个元素返回相同值,它们即被视为同一组,返回值作为组的键。例如,若需忽略大小写进行分组,可使元素'A'和'a'返回相同的键:

>>> for key, group in itertools.groupby('AaaBBbcCAAa', lambda c: c.upper()):
...   print key, list(group)
...
A ['A', 'a', 'a']
B ['B', 'B', 'b']
C ['c', 'C']
A ['A', 'A', 'a']

imap()函数

imap()与map()的主要区别在于,imap()支持处理无限序列,且当两个序列长度不一致时,以较短的序列为准。

>>> for x in itertools.imap(lambda x, y: x * y, [10, 20, 30], itertools.count(1)):
...   print x
...
10
40
90

需注意,imap()返回的是迭代对象,而map()直接返回list。调用map()时,计算已立即完成:

>>> r = map(lambda x: x*x, [1, 2, 3])
>>> r # r已经计算出来了
[1, 4, 9]

调用imap()时,并未执行任何实际计算:

>>> r = itertools.imap(lambda x: x*x, [1, 2, 3])
>>> r
<itertools.imap object at 0x103d3ff90>
# r只是一个迭代对象

必须通过for循环对迭代对象r进行遍历,才能在每次循环中动态计算下一个元素:

>>> for x in r:
...   print x
...
1
4
9

这表明imap()实现了“惰性计算”,即仅在需要结果时才进行计算。具备此类惰性计算特性的函数能够处理无限序列:

>>> r = itertools.imap(lambda x: x*x, itertools.count(1))
>>> for n in itertools.takewhile(lambda x: x<100, r):
...   print n
...

最终输出结果是什么?

若将imap()替换为map()来处理无限序列,会产生什么结果?

>>> r = map(lambda x: x*x, itertools.count(1))

最终输出结果是什么?

ifilter()函数

无需多言,ifilter()即为filter()的惰性实现版本。 小结

itertools模块提供的所有函数均专注于处理迭代功能,其返回值均为迭代对象,而非列表。只有在通过for循环进行迭代时,才会真正执行计算。

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表