Python 优化对对象列表的所有元素调用方法

Python 优化对对象列表的所有元素调用方法,python,optimization,Python,Optimization,我有一个对象列表,我想在列表中循环并为每个对象调用一个方法。显然,这是过度优化,更多的意思是“哦,我想知道什么是最快的方式”,而不是“必须有每一个周期的速度值”。我可以想出几种方法来实现这一点,并对它们进行了分析,得出以下结果: ; ipython Python 2.7.5 (default, Jun 17 2014, 18:11:42) Type "copyright", "credits" or "license" for more information. IPython 2.3.0

我有一个对象列表,我想在列表中循环并为每个对象调用一个方法。显然,这是过度优化,更多的意思是“哦,我想知道什么是最快的方式”,而不是“必须有每一个周期的速度值”。我可以想出几种方法来实现这一点,并对它们进行了分析,得出以下结果:

; ipython
Python 2.7.5 (default, Jun 17 2014, 18:11:42) 
Type "copyright", "credits" or "license" for more information.

IPython 2.3.0 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

 In [1]  class ook:
             def eek(self):
                 pass
             

 In [2]  lst = [ook(), ook(), ook(), ook(), ook(), ook(), ook(), ook()]

 In [3]  %timeit -n 10000 map(lambda x: x.eek(), lst)
10000 loops, best of 3: 2.17 µs per loop

 In [4]  %timeit -n 10000 [x.eek() for x in lst]
10000 loops, best of 3: 2.32 µs per loop

 In [5]  def test(lst):
             for x in lst:
                 x.eek()
                 

 In [6]  %timeit -n 10000 test(lst)
10000 loops, best of 3: 2.18 µs per loop
标准循环方法在可读性方面似乎是成功的,但是
map
解决方案看起来要快一点

我错过什么了吗

有更好的方法吗?

使用更大的数组(1000个ook),普通循环更快(在我的测试中),然后是列表理解,然后是map方法(慢60%)

普通循环速度最快的原因(可能)是它不必收集新列表的返回值。映射速度如此之慢的原因是它需要为每次迭代调用一个额外的函数(lambda)

然而,在现实生活中,与实际的
eek()
方法相比,这些方法的开销可能微不足道。

使用更大的数组(1000个ook),普通循环更快(在我的测试中),其次是列表理解,然后是map方法(慢60%)

普通循环速度最快的原因(可能)是它不必收集新列表的返回值。映射速度如此之慢的原因是它需要为每次迭代调用一个额外的函数(lambda)


然而,在现实生活中,与实际的
eek()
方法相比,这些方法的开销可能微不足道。

如果您只想调用列表中的所有函数,for循环应该是最有效的,因为它不会创建新的列表(它实际上是我的机器上效率最高的)


另外请注意,在Python3中,映射解决方案不起作用,因为它创建的生成器将在稍后进行评估。

如果您只希望调用列表中的所有函数,for循环应该是最有效的,因为它不会创建新的列表(实际上它在我的机器上是最有效的)


另外请注意,在Python 3中,映射解决方案不起作用,因为它创建的生成器将在稍后进行评估。

@Sandathrion,在回答您在评论中的附加问题“您是否可以创建一个答案,说明如何使用上面的玩具示例实现此目的?”这很难,因为玩具示例没有做任何事情,但是如果使用一个几乎同样简单的示例将类属性从0更改为1000个对象中的1:

import timeit

### values held externally in numpy array
setup = """
import numpy as np
N = 1000
class ook(object):
  def __init__(self, i):
    self.ook_index = i

ook_vals = np.zeros(N)

lst = [ook(i) for i in range(N)]
"""
fn = """
ook_vals[:] = 1
"""
print(timeit.timeit(fn, setup, number=10000) / 10000)

### values held in class instances
setup = """
N = 1000
class ook(object):
  def __init__(self):
    self.val = 0
  def eek(self):
    self.val = 1

lst = [ook() for i in range(N)]
"""
fn = """
for x in lst:
  x.eek()
"""
print(timeit.timeit(fn, setup, number=10000) / 10000)
结果是

3.43601703644e-06
0.00044386138916

i、 e.numpy方法比Sandathrion快了两个数量级,这是对您在评论中的附加问题的回答,“您能用上面的玩具示例创建一个如何实现这一点的答案吗?”这很难,因为玩具示例没有做任何事情,但是如果使用一个几乎同样简单的示例将类属性从0更改为1000个对象中的1:

import timeit

### values held externally in numpy array
setup = """
import numpy as np
N = 1000
class ook(object):
  def __init__(self, i):
    self.ook_index = i

ook_vals = np.zeros(N)

lst = [ook(i) for i in range(N)]
"""
fn = """
ook_vals[:] = 1
"""
print(timeit.timeit(fn, setup, number=10000) / 10000)

### values held in class instances
setup = """
N = 1000
class ook(object):
  def __init__(self):
    self.val = 0
  def eek(self):
    self.val = 1

lst = [ook() for i in range(N)]
"""
fn = """
for x in lst:
  x.eek()
"""
print(timeit.timeit(fn, setup, number=10000) / 10000)
结果是

3.43601703644e-06
0.00044386138916

i、 e.numpy方法快了几个数量级

我个人会使用
map
,但它取决于问题域,因为一些操作可以矢量化,这会导致numpy/pandas对象成为我的“标准循环”解决方案的速度几乎快了整整一微秒。但是我忍不住认为这是过度优化;选择一个可读性最好的解决方案。如果速度很重要,那么最好重新构建问题,以便使用numpy@paddyg或者C++如果速度很重要,我会使用<代码> map < /C>,但是它取决于问题域的大小。ome操作可以矢量化,这将导致numpy/pandas对象成为“标准循环”解决方案的速度几乎快了整整一微秒。但是我忍不住认为这是过度优化;选择一个可读性最好的解决方案。如果速度很重要,那么最好重新构建问题,以便使用numpy@paddyg或者C++如果速度非常重要,非常感谢。超级。非常感谢。