为什么map和filter函数在python3中返回迭代器?

时间:2018-08-05 14:18:59

标签: python

为什么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>

2 个答案:

答案 0 :(得分:1)

filtermap返回迭代器。您可以通过包装列表将它们转换为列表

numbers = [1,2,3,4,5]
list(map(lambda x:x*x , numbers))
# [1, 4, 9, 16, 25]

为什么与reduce不一致?因为reduce返回单个值。返回一个迭代器是没有意义的。


为什么mapfilter不返回列表?好吧,他们曾经使用过,但是后来由于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)

mapfilter操作返回(惰性)可迭代的原因是,有时不需要分配列表。生成列表需要计算所有结果,并分配可能无用的内存。


无用的内存分配:

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 % 21 % 2。这样可以节省CPU,但在许多实际示例中可以减少IO和内存消耗。


当您需要结果时,可以自行决定以何种方式收集结果。该集合与计算是分开的,自python3以来,这些计算是通过迭代器懒惰地实现的。