为什么map和filter函数在python3中返回迭代器,而reduce本身返回fold操作?通过返回迭代器,我们可以获得所有性能/内存提升?
地图返回“地图”对象:
>>> numbers = [1,2,3,4,5]
>>> map(lambda x:x*x , numbers)
<map object at 0x103e01f28>
Reduce直接向我返回折叠操作本身的结果
>>> words = ['apple', 'pineapple', 'pear', 'mango']
>>> reduce(lambda word1,word2: word1 if len(word1) > len(word2) else word2 ,words)
'pineapple'
过滤器再次返回“ filter:object:
>>> numbers = [1,2,3,4,5]
>>> filter(lambda x:x%2 == 0,numbers)
<filter object at 0x103e07c18>
答案 0 :(得分:1)
filter
和map
返回迭代器。您可以通过包装列表将它们转换为列表
numbers = [1,2,3,4,5]
list(map(lambda x:x*x , numbers))
# [1, 4, 9, 16, 25]
为什么与reduce
不一致?因为reduce
返回单个值。返回一个迭代器是没有意义的。
为什么map
和filter
不返回列表?好吧,他们曾经使用过,但是后来由于python 3而被更改了,这是有充分理由的。 Lazy iterators很有用,因为我们不会在每次使用一个N
值时创建一个新列表。这意味着它们可以按顺序使用,例如
map(operator.truediv, filter(lambda t: t[1] != 0, zip(itr1, itr2)))
,无需创建不必要的大数据块。
要在评论中回答问题:
否,map
不仅在以上示例中的filter
完成之后应用。证明:
def verbose_count():
i = 0
while True:
print(f'generating {i}')
yield i
i += 1
itr = map(lambda x: x * x, filter(lambda x: x % 2 == 0, verbose_count()))
next(itr)
generating 0
Out[2]: 0
next(itr)
generating 1
generating 2
Out[3]: 4
如您所见,当我没有传递所有值时,我可以获得filter -> map
序列的输出(我甚至传递了一个永远不会完成的无限生成器)。
答案 1 :(得分:1)
map
和filter
操作返回(惰性)可迭代的原因是,有时不需要分配列表。生成列表需要计算所有结果,并分配可能无用的内存。
无用的内存分配:
sum(map(math.pow, range(10), repeat(2)))
由于sum
实际上是部分reduce(operator.add, iterator, 0)
,因此不需要保留整个数据。在此示例中,如果map将创建列表,则它将不必要地分配内存。另外,如果列表不适合内存,它将不起作用(与惰性版本不同)。请参见以下示例,该示例在python2中会崩溃,但由于懒惰而在python3中有效:
any(map(math.sqrt, range(99999999999999999999)))
无用的结果计算:
def is_odd(n):
return n % 2
first_odd = next(filter(is_odd, range(10)))
在此示例中,如果filter
将计算所有结果,则它将丢弃几乎整个列表,因为只有第一个奇数(1)是有趣的。在这种情况下,实际上仅计算0 % 2
和1 % 2
。这样可以节省CPU,但在许多实际示例中可以减少IO和内存消耗。
当您需要结果时,可以自行决定以何种方式收集结果。该集合与计算是分开的,自python3以来,这些计算是通过迭代器懒惰地实现的。