在分析我的Python应用程序时,我发现使用集合时len()
似乎非常昂贵。请参阅以下代码:
import cProfile
def lenA(s):
for i in range(1000000):
len(s);
def lenB(s):
for i in range(1000000):
s.__len__();
def main():
s = set();
lenA(s);
lenB(s);
if __name__ == "__main__":
cProfile.run("main()","stats");
根据以下个人资料的统计数据,lenA()
似乎比lenB()
慢14倍:
ncalls tottime percall cumtime percall filename:lineno(function)
1 1.986 1.986 3.830 3.830 .../lentest.py:5(lenA)
1000000 1.845 0.000 1.845 0.000 {built-in method len}
1 0.273 0.273 0.273 0.273 .../lentest.py:9(lenB)
我错过了什么吗?目前我使用的是__len__()
而不是len()
,但代码看起来很脏:(
答案 0 :(得分:17)
显然,len
有一些开销,因为它执行函数调用并将AttributeError
转换为TypeError
。此外,set.__len__
是一个如此简单的操作,与几乎任何东西相比,它必然会非常快,但在使用timeit
时我仍然没有找到类似14x的差异:
In [1]: s = set()
In [2]: %timeit s.__len__()
1000000 loops, best of 3: 197 ns per loop
In [3]: %timeit len(s)
10000000 loops, best of 3: 130 ns per loop
您应始终致电len
,而不是__len__
。如果对len
的调用是您程序中的瓶颈,则应重新考虑其设计,例如:缓存大小某处或计算它们而不调用len
。
答案 1 :(得分:5)
这是关于分析器的有趣观察,它与len函数的实际性能无关。您可以在分析器统计信息中看到有关lenA
的两行:
ncalls tottime percall cumtime percall filename:lineno(function)
1 1.986 1.986 3.830 3.830 .../lentest.py:5(lenA)
1000000 1.845 0.000 1.845 0.000 {built-in method len}
...虽然lenB
只有一行:
1 0.273 0.273 0.273 0.273 .../lentest.py:9(lenB)
个人资料分析器将每个来电从lenA
定时到len
,但整个时间lenB
。定时呼叫总是增加一些开销;在lenA的情况下,你看到这个开销乘以一百万次。
答案 2 :(得分:1)
这将是一个评论,但在larsman评论他的争议结果和我得到的结果之后,我认为将我的数据添加到帖子中很有意思。
尝试或多或少相同的设置我得到了相反的OP得到了,并且在larsman评论的同一方向:
12.1964105975 <- __len__
6.22144670823 <- len()
C:\Python26\programas>
测试:
def lenA(s):
for i in range(100):
len(s);
def lenB(s):
for i in range(100):
s.__len__();
s = set()
if __name__ == "__main__":
from timeit import timeit
print timeit("lenB(s)", setup="from __main__ import lenB, s")
print timeit("lenA(s)", setup="from __main__ import lenA, s")
这是win7中的activepython 2.6.7 64位