小编典典

为什么 [] 比 list() 快?

all

我最近比较了 和 的处理速度,[]惊讶list()地发现它的[]运行 速度list().
我用{}and进行了相同的测试,dict()结果几乎相同:[]两者{}都花费了大约 0.128 秒 /
百万个周期,而list()每个dict()大约花费了 0.428 秒 / 百万个周期。

为什么是这样?[]{}(可能()和,''也可能)立即传回一些空的股票文字的副本,而它们的明确命名的对应物(list(),
dict(), tuple(), str())完全开始创建一个对象,无论它们是否真的有元素?

我不知道这两种方法有何不同,但我很想知道。我在文档或 SO 上找不到答案,而且搜索空括号的问题比我预期的要严重。

timeit.timeit("[]")我通过调用and timeit.timeit("list()")
andtimeit.timeit("{}")timeit.timeit("dict()"), 分别比较列表和字典来得到我的计时结果。我正在运行
Python 2.7.9。

我最近发现了“为什么 if True 比 if 1
慢?
”它比较了if Trueto的性能,if 1似乎触及了类似的文字与全局场景;也许它也值得考虑。


阅读 181

收藏
2022-03-02

共1个答案

小编典典

因为[]and{}字面语法 。Python 可以创建字节码来创建列表或字典对象:

>>> 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()是单独的对象。需要解析它们的名称,必须涉及堆栈以推送参数,必须存储框架以供以后检索,并且必须进行调用。这一切都需要更多时间。

对于空的情况,这意味着您至少有 a
LOAD_NAME(必须搜索全局命名空间以及builtinsmodule)后跟
a CALL_FUNCTION,它必须保留当前帧:

>>> 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

但你永远无法克服这个CALL_FUNCTION成本。

2022-03-02