itertools中chain和chain.from_iterable有什么区别?

88 投票
6 回答
43415 浏览
提问于 2025-04-17 16:38

我在网上找不到任何有效的例子,能让我看到它们之间的区别,以及为什么要选择其中一个而不是另一个。

6 个回答

9

这两个方法做的事情很相似。当你处理的可迭代对象(比如列表、元组等)数量不多时,itertools.chain(*iterables)itertools.chain.from_iterable(iterables) 的表现差不多。

不过,from_iterables 的一个主要优点是它可以处理很多(甚至是无限)可迭代对象,因为在调用的时候并不需要所有的可迭代对象都准备好。

19

我找不到任何有效的例子……让我能看到chainchain.from_iterable之间的区别,以及为什么要选择其中一个。

被接受的答案很详细。对于那些想快速应用的人,可以考虑把几个列表合并成一个列表:

list(itertools.chain(["a", "b", "c"], ["d", "e"], ["f"]))
# ['a', 'b', 'c', 'd', 'e', 'f']

你可能希望以后再使用这些列表,所以你可以把它们放在一个可迭代的列表中:

iterable = (["a", "b", "c"], ["d", "e"], ["f"])

尝试

但是,把一个可迭代对象传给chain会得到一个没有合并的结果:

list(itertools.chain(iterable))
# [['a', 'b', 'c'], ['d', 'e'], ['f']]

为什么呢?因为你传入了一个项目(一个元组)。chain需要每个列表单独传入。


解决方案

如果可能的话,你可以展开一个可迭代对象:

list(itertools.chain(*iterable))
# ['a', 'b', 'c', 'd', 'e', 'f']

list(itertools.chain(*iter(iterable)))
# ['a', 'b', 'c', 'd', 'e', 'f']

更一般来说,使用.from_iterable(因为它也适用于无限迭代器):

list(itertools.chain.from_iterable(iterable))
# ['a', 'b', 'c', 'd', 'e', 'f']

g = itertools.chain.from_iterable(itertools.cycle(iterable))
next(g)
# "a"
100

第一个形式可以接收0个或多个参数,每个参数都是一个可迭代对象;而第二个形式只接收一个参数,这个参数应该能生成可迭代对象:

from itertools import chain

chain(list1, list2, list3)

iterables = [list1, list2, list3]
chain.from_iterable(iterables)

不过,iterables可以是任何能产生可迭代对象的迭代器:

def gen_iterables():
    for i in range(10):
        yield range(i)

itertools.chain.from_iterable(gen_iterables())

使用第二种形式通常是为了方便,但因为它是懒惰地遍历输入的可迭代对象,所以这是唯一可以串联无限多个有限迭代器的方法:

def gen_iterables():
    while True:
        for i in range(5, 10):
            yield range(i)

chain.from_iterable(gen_iterables())

上面的例子会给你一个可迭代对象,它会不断产生一个循环的数字模式,这个模式永远不会停止,但它所占用的内存不会超过一次range()调用所需的内存。

撰写回答