在Python 3中描述len(set)与set .__ len __()的性能

时间:2012-01-08 15:24:03

标签: python performance profiling set

在分析我的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(),但代码看起来很脏:(

3 个答案:

答案 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位