【问题标题】:Python: List Comprehensions vs. mapPython:列表理解与地图
【发布时间】:2012-07-23 16:28:22
【问题描述】:

参考这个Python List Comprehension Vs. Map 问题,有人可以解释为什么当列表理解不调用函数时,列表理解比map 给出更好的结果,即使map 中没有lambda 函数但给出最差的结果调用函数时?

import timeit

print timeit.Timer('''[i**2 for i in xrange(100)]''').timeit(number = 100000)

print timeit.Timer('''map(lambda i: i**2, xrange(100))''').timeit(number = 100000)

print timeit.Timer(setup="""def my_pow(i):
    return i**2
""",stmt="""map(my_pow, xrange(100))""").timeit(number = 100000)

print timeit.Timer(setup="""def my_pow(i):
    return i**2
""",stmt='''[my_pow(i) for i in xrange(100)]''').timeit(number = 100000)

结果:

1.03697046805 <-- list comprehension without function call
1.96599485313 <-- map with lambda function
1.92951520483 <-- map with function call
2.23419570042 <-- list comprehension with function call

【问题讨论】:

  • map 中调用的函数是 lambda 函数还是常规函数都没有关系,开销仍然存在。不知道为什么列表理解 with 函数调用会比 map() 慢。
  • @millimoose 但是每次迭代都会声明 lambda 函数,这有什么改变吗?
  • @zenpoy:函数调用参数在函数被调用之前被评估,所以函数只声明一次。
  • 无法删除我手机上的 cmets :(
  • @SvenMarnach 我认为他/她在谈论 my_pow 定义在整个 timeit 执行(在设置中)只解释一次,并且 lambda 为每次迭代定义。这是一个有效的问题,而 lambda 可能导致它的版本稍慢。

标签: python performance


【解决方案1】:

你所有的计时结果都可以用这些事实来解释:

  1. CPython 有相当高的函数调用开销。

  2. map(f, it)[f(x) for x in it] 稍快。

您的代码的第一个版本根本没有定义函数,因此没有函数调用开销。第二个版本需要定义一个函数,所以每次迭代都有函数调用开销。第三个版本完全等同于第二个版本——函数和 lambda 表达式是一回事。根据事实 2,最后一个版本甚至更慢。

【讨论】:

  • 我可以想到事实 (2) 的两个可能原因:map 的循环直接在 C 中进行,因此循环中的开销略少,并且对名称 f 的引用map 只需要解决一次,但在列表理解的每次迭代中。第二个对我来说更重要,但有什么方法可以判断哪个贡献更大?
  • @Dougal:这不是有人第一次提到在 C 中发生的循环,但我从来没有找到参考或其他东西来支持它应该更快。
  • @RikPoggi 我尝试检查 Python 源代码,而 3.x 中的 map 实际上返回一个迭代器,并使用参数的迭代器,这使得该理论不太可能。所有这些都是基于 C 的,可能仍然有一些节省,但没有神话般的“C 循环”。实际上比较 2.x 和 3.x 在这方面的性能可能会很有趣(如果没有任何结论的话)。
  • @millimoose:诸如“循环完全在 C 中完成”之类的事情通常意味着执行循环不需要 Python 字节码评估。
  • @SvenMarnach 很公平,但这种区别会产生重大影响吗?运行时仍然必须处理操作解释器状态的所有开销;在我看来,字节码调度本身并不是造成放缓的主要原因。
猜你喜欢
  • 2011-09-02
  • 2010-11-17
  • 1970-01-01
  • 2017-11-25
  • 2023-03-29
  • 1970-01-01
  • 2023-04-08
相关资源
最近更新 更多