因为[]和{}是字面语法. 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()是单独的对象.需要解析它们的名称,必须涉及堆栈以推送参数,必须存储帧以便稍后检索,并且必须进行调用.这都需要更多时间.
对于空案例,这意味着你至少有一个LOAD_NAME(必须搜索全局命名空间以及__builtin__ module),然后是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秒.
您可以通过将全局名称别名为locals来避免全局查找成本(使用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成本.