为什么[]比list()更快?

2024-04-19 07:22:56 发布

您现在位置:Python中文网/ 问答频道 /正文

我最近比较了[]list()的处理速度,惊奇地发现[]的运行速度是list()的三倍多。我对{}dict()进行了相同的测试,结果几乎相同:[]{}都花费了大约0.128秒/百万个周期,而list()dict()每个周期大约花费了0.428秒/百万个周期。

这是为什么?[]{}(可能还有()'')会立即传回一些空的stock文本的副本,而它们的显式命名对应项(list()dict()tuple()str())会完全着手创建一个对象,不管它们实际上是否有元素?

我不知道这两种方法有什么不同,但我很想知道。 我在文档中找不到答案,搜索空括号的问题比我预想的要多。

我通过分别调用timeit.timeit("[]")timeit.timeit("list()"),以及timeit.timeit("{}")timeit.timeit("dict()")来比较列表和字典来获得计时结果。我在运行Python2.7.9。

我最近发现了“Why is if True slower than if 1?”,它比较了if Trueif 1的性能,似乎触及了类似的文字和全局场景;也许也值得考虑。


Tags: 对象文本trueifstock副本命名速度
3条回答

list()需要全局查找和函数调用,但[]编译为单个指令。见:

Python 2.7.3
>>> import dis
>>> print dis.dis(lambda: list())
  1           0 LOAD_GLOBAL              0 (list)
              3 CALL_FUNCTION            0
              6 RETURN_VALUE        
None
>>> print dis.dis(lambda: [])
  1           0 BUILD_LIST               0
              3 RETURN_VALUE        
None

因为list是一个function来将字符串转换为列表对象,而[]则用于创建一个现成的列表。试试这个(可能对你更有意义):

x = "wham bam"
a = list(x)
>>> a
["w", "h", "a", "m", ...]

y = ["wham bam"]
>>> y
["wham bam"]

给你一个实际的清单,里面有你放进去的东西。

因为[]{}文字语法。Python可以创建字节码来创建list或dictionary对象:

>>> import dis
>>> dis.dis(compile('[]', '', 'eval'))
  1           0 BUILD_LIST               0
              3 RETURN_VALUE        
>>> dis.dis(compile('{}', '', 'eval'))
  1           0 BUILD_MAP                0
              3 RETURN_VALUE        

list()dict()是独立的对象。它们的名字需要被解析,堆栈必须参与到参数的推送中,帧必须被存储以便以后检索,并且必须进行调用。这需要更多的时间。

对于空的情况,这意味着您至少有一个^{}(它必须通过全局命名空间以及^{} module进行搜索),然后是一个^{},它必须保留当前帧:

>>> dis.dis(compile('list()', '', 'eval'))
  1           0 LOAD_NAME                0 (list)
              3 CALL_FUNCTION            0
              6 RETURN_VALUE        
>>> dis.dis(compile('dict()', '', 'eval'))
  1           0 LOAD_NAME                0 (dict)
              3 CALL_FUNCTION            0
              6 RETURN_VALUE        

您可以使用timeit分别计时名称查找:

>>> import timeit
>>> timeit.timeit('list', number=10**7)
0.30749011039733887
>>> timeit.timeit('dict', number=10**7)
0.4215109348297119

时间差异可能是字典哈希冲突。从调用这些对象的时间中减去这些时间,并将结果与使用文字的时间进行比较:

>>> timeit.timeit('[]', number=10**7)
0.30478692054748535
>>> timeit.timeit('{}', number=10**7)
0.31482696533203125
>>> timeit.timeit('list()', number=10**7)
0.9991960525512695
>>> timeit.timeit('dict()', number=10**7)
1.0200958251953125

因此,必须调用对象每1000万次调用需要额外的1.00 - 0.31 - 0.30 == 0.39秒。

通过将全局名称别名为局部名称(使用timeit设置,绑定到名称的所有内容都是局部名称),可以避免全局查找开销:

>>> timeit.timeit('_list', '_list = list', number=10**7)
0.1866450309753418
>>> timeit.timeit('_dict', '_dict = dict', number=10**7)
0.19016098976135254
>>> timeit.timeit('_list()', '_list = list', number=10**7)
0.841480016708374
>>> timeit.timeit('_dict()', '_dict = dict', number=10**7)
0.7233691215515137

但你永远无法克服这种代价。

相关问题 更多 >