itertools.imap 与整个可迭代对象的映射
Posted
技术标签:
【中文标题】itertools.imap 与整个可迭代对象的映射【英文标题】:itertools.imap vs map over the entire iterable 【发布时间】:2013-12-31 16:20:07 【问题描述】:我很好奇http://docs.python.org/2/library/itertools.html#itertools.imap 的声明,即它描述了
sum(imap(operator.mul, vector1, vector2))
作为一种有效的点积。我的理解是 imap 提供了一个生成器而不是一个列表,虽然我理解如果你只考虑前几个元素和周围的 sum() 会更快/消耗更少的内存,但我不明白如何它的行为不同于:
sum(map(operator.mul, vector1, vector2))
【问题讨论】:
请注意,在 Python 3.x 中,itertools.imap
被简单地称为 map
(类似的事情发生在 filter
等处),并获得旧的、返回列表的版本 @ 987654327@你需要做list(map(x))
。
【参考方案1】:
当您开始增加迭代对象的大小时,map
和 imap
之间的区别会变得很明显:
# xrange object, takes up no memory
data = xrange(1000000000)
# Tries to builds a list of 1 billion elements!
# Therefore, fails with MemoryError on 32-bit systems.
doubled = map(lambda x: x * 2, data)
# Generator object that lazily doubles each item as it's iterated over.
# Takes up very little (and constant, independent of data's size) memory.
iter_doubled = itertools.imap(lambda x: x * 2, data)
# This is where the iteration and the doubling happen.
# Again, since no list is created, this doesn't run you out of memory.
sum(iter_doubled)
# (The result is 999999999000000000L, if you're interested.
# It takes a minute or two to compute, but consumes minimal memory.)
请注意,在 Python 3 中,内置 map
的行为类似于 Python 2 的 itertools.imap
(因为不再需要它而被删除)。要获得“旧的map
”行为,您可以使用list(map(...))
,这是另一种可视化Python 2 的itertools.imap
和map
彼此不同的好方法。
【讨论】:
【参考方案2】:第一行将逐个计算累加项目的总和。第二个将首先计算整个点积,然后将整个结果存储在内存中,然后继续计算总和。因此,内存复杂度有所增加。
【讨论】:
【参考方案3】:要注意的另一件事是“使用更少的内存”通常也意味着“运行得更快”。惰性(迭代器)版本在计算后立即使用每个产品,并将其添加到运行总和中。几乎可以肯定,乘积和运行总和都在 L1 缓存中。如果您首先计算所有产品,那么根据元素的数量,可以肯定计算的第一个产品将被踢出 L1 缓存,然后被踢出 L2 缓存,并且......这样当第二次通过时最后将它们加在一起,所有产品在内存层次结构中都很低(并且在极端情况下,必须从页面文件中读取)。
但我不清楚您所说的“看不到它的行为方式有何不同”是什么意思。最终的计算结果都是一样的。
【讨论】:
【参考方案4】:不同之处在于imap(...)
或map(...)
的整个输出都传递给sum()
。您写道imap
返回了一个生成器,但我认为您可能认为sum(map(...))
有一些捷径可以做同样的事情。它没有。 map()
将构造一个完整的结果列表在任何东西被传递给sum()
。
【讨论】:
以上是关于itertools.imap 与整个可迭代对象的映射的主要内容,如果未能解决你的问题,请参考以下文章
python map vs itertools.map:使迭代器版本表现得像前者