chain(*iterable) 与 chain.from_iterable(iterable) 之间的区别

Posted

技术标签:

【中文标题】chain(*iterable) 与 chain.from_iterable(iterable) 之间的区别【英文标题】:Difference between chain(*iterable) vs chain.from_iterable(iterable) 【发布时间】:2016-09-16 13:44:41 【问题描述】:

我一直对itertools 中所有有趣的迭代器非常着迷,但我有一个困惑是这两个函数之间的区别以及chain.from_iterable 存在的原因。

from itertools import chain

def foo(n):
    for i in range(n):
        yield [i, i**2]

chain(*foo(5))

chain.from_iterable(foo(5))

这两个函数有什么区别?

【问题讨论】:

【参考方案1】:

前者只能处理不可打包的迭代。后者可以处理无法完全解包的可迭代对象,例如无限生成器。

考虑

>>> from itertools import chain
>>> def inf():
...     i=0
...     while True:
...         i += 1
...         yield (i, i)
... 
>>> x=inf()
>>> y=chain.from_iterable(x)
>>> z=chain(*x)
<hangs forever>

此外,拆包是一项急切的预付费活动,因此,如果您的可迭代对象具有您想要延迟评估的效果,from_iterable 是您的最佳选择。

【讨论】:

顺便说一句,这个例子中的inf实际上和itertools.count是一样的 这里的示例代码无论如何都不起作用; inf 不是可迭代的可迭代,它是标量的可迭代,因此只有 chain(x) 可以工作。 chain(*x) 确实更糟(它甚至无法成功拨打电话,chain.from_iterable(x) 会立即返回,并且只有在您向它请求值时才会以 TypeError 死掉),但这两个例子都很糟糕。 yield (i, i) 或其他东西会使这个例子变得理智。【参考方案2】:

chain(*foo(5)) 解包整个生成器,将其打包成一个元组,然后对其进行处理。

chain.from_iterable(foo(5)) 查询从foo(5) 值创建的生成器。

尝试foo(1000000) 并观察内存使用量不断上升。

【讨论】:

【参考方案3】:

* 解包迭代器,这意味着它迭代迭代器以便将其值传递给函数。 chain.from_iterable 懒惰地一一迭代迭代器。

【讨论】:

以上是关于chain(*iterable) 与 chain.from_iterable(iterable) 之间的区别的主要内容,如果未能解决你的问题,请参考以下文章

为啥chain.from_iterable 不遍历int 列表?

from itertools import chain:使用详解

chain.from_iterable实现同时walk多个目录

python 迭代器之chain

Flink collector 非常慢 一次尴尬的 排查错误方向 chain 与 不chain 的 区别

itertools内置库