2013-11-22 33 views
2

当我以不同的方式构建列表时,我注意到了一些有趣的行为。 .append花费的时间比列表解析,这需要比map长更长的时间,如下面的实验:循环结构中的加速

def square(x): return x**2 

def appendtime(times=10**6): 
    answer = [] 
    start = time.clock() 
    for i in range(times): 
    answer.append(square(i)) 
    end = time.clock() 
    return end-start 

def comptime(times=10**6): 
    start = time.clock() 
    answer = [square(i) for i in range(times)] 
    end = time.clock() 
    return end-start 

def maptime(times=10**6): 
    start = time.clock() 
    answer = map(square, range(times)) 
    end = time.clock() 
    return end-start 

for func in [appendtime, comptime, maptime]: 
    print("%s: %s" %(func.__name__, func())) 

的Python 2.7:

appendtime: 0.42632 
comptime: 0.312877 
maptime: 0.232474 

的Python 3.3.3:

appendtime: 0.614167 
comptime: 0.5506650000000001 
maptime: 0.57115 

现在,我非常清楚python 2.7中的range会建立一个列表,所以我得到为什么在相应函数的时间之间存在差距python 2.7和3.3。我更关心的是append,列表理解和map之间的相对时间差异。首先,我认为这可能是因为map和列表解析可以让解释者知道结果列表的最终大小,这将允许解释器malloc一个足够大的C数组来存储这个数组,名单。按照这个逻辑,列表解析和map应该花费几乎相同的时间量。但是,时序数据显示在python 2.7中,listcomps的速度是〜1.36x的一样快,与append一样快,map的速度是listcomps的1.34倍。
更奇怪的是,在python 3.3中,listcomps的速度是append的1.12倍,而map实际上比listcomps的要慢。

很明显,map和listcomps不“玩相同的规则”;很明显,地图利用了列表广告所不具备的功能。
有没有人可以揭示这些时间价值差异背后的原因?

+4

强制性“你为什么不使用'timeit'?这对基准测试来说更好。”评论。 – delnan

+0

@delnan - 我也在想这个:) – mgilson

回答

1

首先,在python3.x中,map返回一个iterable,不是一个列表,因此解释了那里的50kx加速。为了使它成为一个公平的时机,在python3.x中,你需要list(map(...))

其次,.append会比较慢,因为每次通过循环,解释器需要查找列表,然后它需要在列表中查找append函数。这个额外的.append查找不需要与list-comp或map一起发生。

最后,在列表理解的情况下,我认为在循环的每一个回合都需要查看函数square。使用地图时,只有当你打电话给地图时才会抬头,这就是为什么如果你在列表理解中调用一个函数,map通常会更快。请注意,虽然列表理解通常击败maplambda功能。

+0

@ inspectorG4dget - 是的,我增加了更多的尝试来解释这种差异。 – mgilson

+0

我已经用'list(map(...))'更新了我的帖子,这看起来更慢。你能解决这个问题吗? – inspectorG4dget

+0

@ inspectorG4dget - 不确定我可以。这可能是因为在这种情况下,你实际上需要调用'map(...).__ iter__',然后'__next__'函数很多次(你没有使用list-comp)。无论如何,你的差异只有〜5%,这是相当小的。 – mgilson