Python itertools.chain 函数
最后修改于 2025 年 3 月 29 日
itertools.chain 函数是 Python 中用于处理可迭代对象的强大工具。它能够高效地连接多个序列,而无需生成中间列表,为处理大型数据集提供了内存高效的解决方案。
与会生成新内存对象的传统连接方式不同,chain 通过迭代器按顺序产生每个输入可迭代对象的元素,将它们视为一个统一的序列。本指南将通过实用且具有说明性的示例全面探讨 chain。
基本 Chain 用法
chain 的核心功能是轻松地将多个可迭代对象合并为一个迭代器。
from itertools import chain
nums = [1, 2, 3]
chars = ('a', 'b', 'c')
unique = {10, 20}
combined = chain(nums, chars, unique)
print(list(combined)) # [1, 2, 3, 'a', 'b', 'c', 10, 20]
此示例突出了 chain 的关键特性。它无缝地集成了列表、元组和集合等不同类型的可迭代对象。每个可迭代对象内的元素顺序得以保留,尽管集合没有固定的顺序。生成的迭代器需要被消费,例如使用 list(),才能查看其内容。
Chain 与列表连接的对比
与标准的列表连接相比,使用 chain 提供了更优越的内存效率,尤其是在处理大型序列时。
from itertools import chain import sys big_seq1 = list(range(50000)) big_seq2 = list(range(50000, 100000)) # Traditional concatenation merged = big_seq1 + big_seq2 print(sys.getsizeof(merged)) # High memory footprint # Using chain linked = chain(big_seq1, big_seq2) print(sys.getsizeof(linked)) # Minimal memory use
传统的连接方式会创建一个包含所有元素的新列表,从而占用大量内存。相反,chain 会生成一个轻量级的迭代器,在需要之前不会复制数据。其内存使用量与输入大小无关,保持恒定,使其成为大规模操作的理想选择。
Chain.from_iterable
当处理包含多个可迭代对象的迭代器时,chain.from_iterable 方法表现出色。
from itertools import chain pairs = [[1, 2], [3, 4], [5, 6]] # chain.from_iterable simplifies flattening flattened = chain.from_iterable(pairs) print(list(flattened)) # [1, 2, 3, 4, 5, 6]
当处理动态创建的序列、处理嵌套结构或在一级嵌套中进行展平时,而事先不知道可迭代对象的数量时,此方法非常有用。它消除了手动解包的需要,简化了整个过程。
连接不同的可迭代对象类型
chain 能够熟练地将各种可迭代对象类型统一到一个序列中。
from itertools import chain
text = "xyz"
numbers = [10, 20]
pairs = ((1, 2), (3, 4))
keys = {"p": 5, "q": 6}
mixed = chain(text, numbers, pairs, keys)
print(list(mixed)) # ['x', 'y', 'z', 10, 20, (1, 2), (3, 4), 'p', 'q']
在组合不同类型时,字符串会产生单个字符,字典会提供其键,而集合不保证顺序。所有元素都会合并到一个扁平的序列中,尽管类型多样,但仍保持简洁。
Chain 的惰性求值
chain 支持惰性求值,将计算推迟到请求元素时。
from itertools import chain
def gen_squares(n):
print(f"Computing {n} squares")
for i in range(n):
yield i * i
lazy_chain = chain(gen_squares(2), gen_squares(3))
print("Chain created")
for square in lazy_chain:
print(square)
输出显示了惰性行为。
Chain created Computing 2 squares 0 1 Computing 3 squares 0 1 4
chain 中的生成器在开始迭代之前不会进行求值,确保资源仅在需要时使用。
Chain 与无限迭代器
chain 可以无缝地链接有限和无限迭代器。
from itertools import chain, count, cycle
short = [5, 6]
upward = count(10, 2) # 10, 12, 14...
loop = cycle('ab') # 'a', 'b', 'a'...
combined = chain(short, upward, loop)
from itertools import islice
print(list(islice(combined, 8))) # [5, 6, 10, 12, 14, 16, 'a', 'b']
使用无限迭代器时,除非受到控制(例如使用 islice),否则 chain 会无休止地产生项目。有限的可迭代对象会先被消耗,然后按指定顺序消耗无限迭代器。
实际文件处理
chain 可以简化将多个文件视为单个数据流的操作。
from itertools import chain
def file_lines(files):
return chain.from_iterable(open(f) for f in files)
# Process files as one
for line in file_lines(['log1.txt', 'log2.txt']):
print(line.strip())
此方法会惰性地一次打开一个文件,无论文件大小如何,都能保持较低的内存使用量。它提供了一种迭代行的简单方法,并且可以轻松地扩展到更多文件。
嵌套 Chain 操作
嵌套 chain 操作可以处理复杂的分层序列。
from itertools import chain
mixed_data = [[1, 2], [3, [4, 5]], [6]]
# One-level flatten
flat1 = chain.from_iterable(mixed_data)
print(list(flat1)) # [1, 2, 3, [4, 5], 6]
# Full flatten
def deep_flatten(items):
for x in chain.from_iterable(items):
if isinstance(x, list):
yield from deep_flatten([x])
else:
yield x
print(list(deep_flatten(mixed_data))) # [1, 2, 3, 4, 5, 6]
chain.from_iterable 方法仅展平一层,而递归方法则完全展平任何深度的嵌套结构。两者都能有效地保留原始元素顺序。
性能注意事项
chain 在某些情况下可以提供性能优势。
from itertools import chain
import timeit
many_lists = [[i] for i in range(500)]
def via_concat():
result = []
for lst in many_lists:
result += lst
return result
def via_chain():
return list(chain.from_iterable(many_lists))
print("Concat:", timeit.timeit(via_concat, number=1000))
print("Chain:", timeit.timeit(via_chain, number=1000))
chain 方法避免了重复创建列表,在处理大量或大型序列时表现出色。对于小型、固定的集合,连接操作可能更快,但 chain 在内存受限的环境中表现更佳。
最佳实践
- 序列的序列首选 from_iterable: 比解包更简洁
- 用于内存效率: 处理大型数据集时
- 与其他 itertools 结合使用: 例如 islice、takewhile 用于控制
- 记录被链接的源: 以便维护复杂的链接
- 简单情况考虑其他方法: 对于 2-3 个序列,使用 + 可能更清晰
来源
作者
我叫 Jan Bodnar,是一位充满热情的程序员,拥有丰富的编程经验。我自 2007 年以来一直在撰写编程文章。迄今为止,我已撰写了 1,400 多篇文章和 8 本电子书。我在编程教学方面拥有超过十年的经验。
列出所有 Python 教程。