Python内置itertools模块详解:无限迭代与惰性计算
本文深入解析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循环进行迭代时,才会真正执行计算。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









