假设有一个类 A 并且有一个类 A 的实例列表,称为lst
。
假设我们想m
在列表中的每个实例上一遍又一遍地调用 A 类的特定方法 ,(实际示例:entity.update()
游戏循环中的方法)。我们知道执行此操作的简单方法如下:
for obj in lst: obj.m()
然而,这种代码让我们睡着了。于是我们想到map
了如下的使用方式:
map(lambda obj: obj.m(), lst)
for
但是我们对上面的代码行进行了几次测试,结果证明它比我们的简单循环慢得多。有时它甚至慢 2 倍。然后我们自己想,“嗯,可能它更慢,因为map
为所有函数调用构造了一个返回值列表并返回该列表”。
假设我们从名为xrange
. 在大多数情况下,我们自己认为,这是一个更酷的版本range
。因此,我们定义了一个名为的函数xmap
,它只是将一个函数应用于对象列表,而无需构造返回值列表并返回它。实现如下:
def xmap(func, lst):
for obj in lst: func(obj)
很酷,因为这个函数只是执行for
上面的循环,只有它允许我们保持幻想并发送我们的 lambda 函数。我们认为这是完美的妥协。但是我们非常细致和小心,所以我们决定制作 2 个脚本来测试我们代码的速度,看看我们是否真的让它比map
.
我们的第一个脚本将简单地使用map
并无用地构建一个我们甚至不需要的列表。
script1.py
:
class A:
def m(self):
pass
lst = [A() for i in xrange(15)]
import time
start = time.time()
for i in xrange(1000000):
map(lambda obj: obj.m(), lst)
print time.time()-start, 'seconds'
我们的第二个脚本将使用xmap
,我们相信它会更快,因为它不必构造一个包含 15 个返回值的列表 1,000,000 次并返回它。
script2.py
def xmap(func, lst):
for obj in lst: func(obj)
class A:
def m(self):
pass
lst = [A() for i in xrange(15)]
import time
start = time.time()
for i in xrange(1000000):
xmap(lambda obj: obj.m(), lst)
print time.time()-start, 'seconds'
最后,我们完成了并且有点兴奋地看到我们的代码会变得多快。然而,在对彼此运行了几次脚本之后,事实证明这script2.py
似乎并不比script1.py
. 实际上,事实证明,script2.py
有时运行时间甚至比script1.py
. xmap
似乎花费的时间或多或少与map
.
为什么我会得到这些结果?
C:\dev\py>python script1.py
14.7799999714 seconds
C:\dev\py>python script2.py
14.2170000076 seconds
C:\dev\py>python script1.py
12.1800000668 seconds
C:\dev\py>python script2.py
12.5759999752 seconds
C:\dev\py>python script1.py
14.3020000458 seconds
C:\dev\py>python script2.py
14.9490001202 seconds
C:\dev\py>python script1.py
14.6879999638 seconds
C:\dev\py>python script2.py
14.3139998913 seconds
我认为我至少会优化一些东西,map
因为我没有构建返回值列表,但我的代码似乎并没有更快。我知道列表构建需要一些时间,因为我已经完成了以下操作:
>>> import timeit
>>> timeit.timeit('[]')
0.1297345953932106
>>> timeit.timeit('[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]')
0.6807682686160632
>>> timeit.timeit('[None,None,None,None,None,None,None,None,None,None,None,None,
None,None,None]')
0.7460120889200539
那么为什么我的xmap
功能似乎没有比 更快map
?