Python 3中len(set)与set._len__;()的评测性能

Python 3中len(set)与set._len__;()的评测性能,python,performance,profiling,set,Python,Performance,Profiling,Set,在分析Python的应用程序时,我发现使用set时,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); i

在分析Python的应用程序时,我发现使用set时,
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倍:


我错过什么了吗?目前我使用
\uuu len\uuu()
而不是
len()
,但代码看起来很脏:(

显然,
len
有一些开销,因为它执行函数调用,并将
AttributeError
转换为
TypeError
。此外,
设置。len\uuuuuuu
是一个非常简单的操作,与几乎任何东西相比,它肯定会非常快,但我在使用时仍然没有发现14倍的差异
timeit

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
,而不是
\uuu len
。如果对
len
的调用是你程序中的瓶颈,你应该重新考虑它的设计,例如在某处缓存大小,或者在不调用
len

的情况下计算它们。这将是一个评论,但在larsman对他有争议的结果和结果发表评论之后根据我得到的结果,我认为将我的数据添加到线程中是很有趣的

尝试或多或少相同的设置我得到了相反的OP得到了,并且在相同的方向上由larsman评论:

12.1964105975   <- __len__
6.22144670823   <- len()

C:\Python26\programas>

这是win7中的activepython 2.6.7 64位。这是关于探查器的一个有趣的观察,它与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

 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)
      1    0.273    0.273    0.273    0.273  .../lentest.py:9(lenB)

探查器已将每个调用从
lenA
计时到
len
,但将
lenB
作为一个整体计时。对调用计时总是会增加一些开销;在lenA的情况下,您会看到此开销成倍增加。

为什么要使用
cProfile
而不是
timeit
?前者用于查找瓶颈在大型程序中,它会牺牲一些小规模上的准确性。后者用于相对精确地测量微小片段的总体性能。
timeit
应该是这样的微基准的首选。对我来说,它表明了一个不太极端的差异(每
len
调用0.0879µs,每
调用0.158µs,每
调用0.158µs,比
len
慢70%)。谢谢@delnan,我是Python新手。使用
timeit
我也得到了类似的比率。的确,我的程序比上面的代码大得多,但是
len()
函数似乎是主要的瓶颈之一。好的,所以我将忽略
len()
并关注我自己的功能,对吗?+1:特别是不要过早优化。基准可能有缺陷,正如您现在可能看到的,三个基准可能会返回三个不同的结果;您可能会使用这样一个微基准来测试与您预期完全不同的东西。显然,
len
不能更快,因为它调用
\uuuu len\uuuuu
。但这是肯定的。@Anony Mouse:事实上,我只是再次查看了我自己的结果,我现在才看到
len
\uuu len\uuuuu
快。不确定这是怎么发生的。
s.\uu len uuuuuu
也进行函数调用,并且必须查找属性。thatt大于
len
@FredFoo:
len()的全局查找
不必查找内置类型的
\uuuu leng\uuu
。事实上,它从不直接查找
\uuu leng\uuu
。它将
tp\u作为\u序列
指针查找,遍历到该结构的
sq\u length
属性。自定义Python类通过查找
\uu leng\uu
属性填充该插槽。对于
 设置
列表
这样,就不需要最后一步了。我认为你的观点绝对准确。这都是关于
cProfile
的开销,而不是
len
函数的性能。
      1    0.273    0.273    0.273    0.273  .../lentest.py:9(lenB)