使用map()来调用列表上的函数或更传统的迭代是不是Pythonic?

时间:2013-10-09 20:19:54

标签: python iteration

我有一个带有方法的对象列表,比如cleanup(),我需要在所有对象上调用它们。我知道至少有两种方式:

map(lambda x: x.cleanup(), _my_list_of_objects)

for x in _my_list_of_objects:
    x.cleanup()

我的问题:

  1. Pythonic是另一个吗?
  2. 还有其他更好的方法吗?我看到前者经常在代码库中使用(在工作中),但它让我觉得地图构造并返回一个然后被丢弃的列表(对吧?)。
  3. 是否需要考虑(直接列出GC的列表)的性能影响?

3 个答案:

答案 0 :(得分:7)

It is not idiomatic使用map,列表推导,生成器表达式 - 或对n值应用某些操作并收集结果的其他任何内容 - 用于副作用。即使您坚持使用不适用的函数式编程概念(即不纯的代码),也存在实际问题:

  • 它会浪费内存和计算累积n个引用(最有可能None),之后会立即丢弃。
  • Python 3 map是懒惰的,所以如果你移植到Python 3,这个代码静默就会停止工作(特别是如果你避免2到3 - 有充分的理由)。
  • 它很可能更慢,不仅仅是由于点1. map必须对每个元素执行一个完整的函数调用,显式循环只需要在字节码级别进行一次跳转。列表理解不需要AFAICT,但在timeit的简短实验中它仍然明显变慢。

如果你想为可迭代的每个元素某事,那就完全 for循环的用途。干净利落。你将不再具有可读性,虽然在某些情况下,在某些Python实现中,更加模糊的变体可能会更快,但它几乎不值得花费可读性(以及验证它实际上是赢的时间!)。

答案 1 :(得分:6)

  1. for - 循环解决方案更好,因为您不关心cleanup的结果。 map是python的功能子集的一部分,使用它创建副作用是一种使用它的坏方法。

    另请注意,map会浪费python2中的空间(因为它会创建一个列表),而在python3中不起作用,您必须使用iterable。在python3中你会有类似的东西:

    from collections import deque
    deque(map(function, iterable), maxlen=0)
    

    为了在没有空间开销的情况下调用函数,我相信这会消除for - 循环可读性的大部分内容。 for - 循环解决方案适用于任何 python版本。

  2. 有很多方法可以做到这一点,但没有比for - 循环更可读的方法,而且没有任何方法可以提供显着的性能优势。

  3. 这取决于列表的大小和具体情况。如果代码处于紧密循环中,那么它可能会产生重大影响。然而,python经过高度优化以处理小对象,因此如果元素数量很少,则操作系统实际上不会执行内存分配。

答案 2 :(得分:0)

所以,我一直使用map()作为'在列表中的一堆对象上调用此函数'的简写。毕竟,列表推导(通常)比在循环中做同样的事情更快,所以为什么不映射?

无论如何,结果如下:

r = [[] for _ in range(10000)]
%timeit map(lambda y: r[y].append(1), x)
100 loops, best of 3: 2.56 ms per loop

r = [[] for _ in range(10000)]
%%timeit
for y in x:
    r[y].append(1)
1000 loops, best of 3: 1.67 ms per loop

对于这种情况,甚至没有关闭 - for循环方法的速度大约是其两倍。