ZetCode

Python itertools.chain 函数

最后修改于 2025 年 3 月 29 日

itertools.chain 函数是 Python 中用于处理可迭代对象的强大工具。它能够高效地连接多个序列,而无需生成中间列表,为处理大型数据集提供了内存高效的解决方案。

与会生成新内存对象的传统连接方式不同,chain 通过迭代器按顺序产生每个输入可迭代对象的元素,将它们视为一个统一的序列。本指南将通过实用且具有说明性的示例全面探讨 chain

基本 Chain 用法

chain 的核心功能是轻松地将多个可迭代对象合并为一个迭代器。

basic_chain.py
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 提供了更优越的内存效率,尤其是在处理大型序列时。

chain_vs_concat.py
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_iterable.py
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 能够熟练地将各种可迭代对象类型统一到一个序列中。

mixed_iterables.py
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 支持惰性求值,将计算推迟到请求元素时。

lazy_evaluation.py
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 可以无缝地链接有限和无限迭代器。

infinite_chains.py
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 可以简化将多个文件视为单个数据流的操作。

file_processing.py
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 操作可以处理复杂的分层序列。

nested_chains.py
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 在某些情况下可以提供性能优势。

performance.py
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 在内存受限的环境中表现更佳。

最佳实践

来源

作者

我叫 Jan Bodnar,是一位充满热情的程序员,拥有丰富的编程经验。我自 2007 年以来一直在撰写编程文章。迄今为止,我已撰写了 1,400 多篇文章和 8 本电子书。我在编程教学方面拥有超过十年的经验。

列出所有 Python 教程