5

I have a list of objects with a method, say cleanup(), that I need to invoke on all of them. I understand there are at least two ways:

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

or

for x in _my_list_of_objects:
    x.cleanup()

My question(s):

  1. Is one more Pythonic than the other?
  2. Are there other better ways? I see the former being used often in the code base (at work) here, but it bugs me that map constructs and returns a list that is then thrown away (right?).
  3. Is the perf impact (of a list that is immediately GC'd) something to consider?
4

3 回答 3

7

使用、列表推导map、生成器表达式——或对 n 值应用一些操作并收集结果的任何其他东西——用于副作用是不习惯的。即使您坚持使用不适用的函数式编程概念(即在不纯代码中),也存在实际问题:

  • 它浪费内存和计算积累 n 个引用(很可能是None),这些引用将在之后立即被丢弃。
  • Python 3map是懒惰的,因此如果您移植到 Python 3,此代码会静默停止工作(尤其是如果您避免使用 2to3 - 有充分的理由)。
  • 它很可能更慢,不仅因为第 1 点。map必须对每个元素执行完整的函数调用,显式循环只需要在字节码级别进行一次跳转。列表理解不需要那个 AFAICT,但在我对timeit.

如果您想为可迭代的每个元素一些事情,这正是for循环的用途。干净利落。您将无法获得更多的可读性,尽管在某些情况下,在某些 Python 实现中更晦涩的变体可能会更快,但在可读性方面付出的代价实际上是不值得的(以及验证它是否真的成功的时间!)。

于 2013-10-09T20:34:36.300 回答
6
  1. -loop 解决方案更好,for因为您不关心cleanup. map是python功能子集的一部分,使用它来创建副作用是一种不好的使用方式。

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

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

    为了在没有空间开销的情况下调用函数,我相信这会失去很多for-loop 的可读性。-loopfor解决方案适用于任何python 版本。

  2. 有很多方法可以做到这一点,但没有一种比for-loop 更具可读性,也没有一种比它提供显着的性能优势。

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

于 2013-10-09T20:33:50.233 回答
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 循环方法的速度大约是原来的两倍。

于 2013-10-10T04:09:57.247 回答