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

Python 为什么[]比list()快?,python,performance,list,instantiation,literals,Python,Performance,List,Instantiation,Literals,我最近比较了[]和list()的处理速度,惊奇地发现[]的运行速度是list()的三倍多。我对{}和dict()进行了相同的测试,结果几乎相同:[]和{}都花费了大约0.128秒/百万个周期,而list()和dict()每个周期大约花费了0.428秒/百万个周期 为什么会这样?当显式命名的副本(list(),dict(),tuple(),str())完全开始创建对象时,请立即传回一些空库存文本的副本,他们是否真的有元素 我不知道这两种方法有什么不同,但我很想知道。 我在文档中找不到答案,搜索空括

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

为什么会这样?当显式命名的副本(
list()
dict()
tuple()
str()
)完全开始创建对象时,请立即传回一些空库存文本的副本,他们是否真的有元素

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

我通过调用
timeit.timeit([])
timeit.timeit(“list()”
)以及
timeit.timeit({}”)
timeit.timeit(“dict()”
)来分别比较列表和字典,从而得到计时结果。我正在运行Python 2.7.9


我最近发现“”比较了
if-True
if-1
的性能,似乎触及到了一个与全局场景类似的文本场景;也许它也值得考虑。

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


为您提供一个实际列表,其中包含您在其中输入的内容。

因为
[]
{}
是文字语法。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()
是独立的对象。需要解析它们的名称,必须涉及堆栈来推送参数,必须存储帧以便稍后检索,并且必须进行调用。这一切都需要更多的时间

对于空的情况,这意味着您至少有一个(必须搜索全局名称空间和)后跟一个,必须保留当前帧:

>>> 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.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
设置,绑定到名称的所有内容都是本地名称):

但是您永远无法克服调用函数的成本。

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

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

这里的答案很好,切中要害,完全涵盖了这个问题。对于那些感兴趣的人,我将从字节码中再往下退一步。我使用的是CPython最近的回购协议;在这方面,旧版本的行为类似,但可能会有细微的变化

下面是每一个函数的执行分解,
BUILD\u LIST
for
[]
CALL\u函数
for
LIST()


你应该只看恐怖:

PyObject*list=PyList_New(oparg);
if(list==NULL)
转到错误;
而(--oparg>=0){
PyObject*item=POP();
PyList_SET_项目(列表、oparg、项目);
}
推送(列表);
分派();
我知道,非常复杂。这是多么简单:

  • 创建一个新的列表(这主要是为一个新的列表对象分配内存),
    oparg
    表示堆栈上的参数数量。直截了当
  • 如果(list==NULL),请检查
    是否正常
    
  • 使用(宏)添加位于堆栈上的任何参数(在本例中不执行)
难怪这么快!它是为创建新列表而定制的,没有其他功能:-)

当您查看代码处理
CALL\u函数时,首先看到的是:

PyObject**sp,*res;
sp=堆栈指针;
res=调用函数(&sp,oparg,NULL);
堆栈指针=sp;
推(res);
如果(res==NULL){
转到错误;
}
分派();
看起来很无害,对吧?嗯,不,不幸的是不是,不是一个会立即调用函数的直截了当的人,它不能。相反,它从堆栈中获取对象,获取堆栈的所有参数,然后根据对象的类型进行切换;它是一个:

  • ??不,它是
    list
    list
    不是
    PyCFunction类型
  • ??不,见上一页
  • ??不,看前面的
我们正在调用
列表
类型,传入
调用函数
的参数为。CPython现在必须调用一个泛型函数来处理任何名为的可调用对象,还有更多的函数调用

这个函数再次对某些函数类型进行一些检查(我不理解为什么),然后在为kwargs创建dict(如果需要)之后,继续调用

\u PyObject\u FastCallDict
终于把我们带到了某处!在执行了更多的
类型的检查之后,我们传入了
类型,也就是说,它获取
类型.tp\u调用
。然后,它继续用
\u PyStack\u AsTuple
传入的参数创建一个元组,最后是

tp\u调用
,它将接管并最终创建列表对象。它调用列表
\uuuu new\uuuu
,该列表对应于并分配内存
>>> 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
>>> 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
Python 2.7.3
>>> import dis
>>> dis.dis(lambda: list())
  1           0 LOAD_GLOBAL              0 (list)
              3 CALL_FUNCTION            0
              6 RETURN_VALUE        
>>> dis.dis(lambda: [])
  1           0 BUILD_LIST               0
              3 RETURN_VALUE        
list_display ::=  "[" [starred_list | comprehension] "]"
class List(list):
    def __init__(self, iterable=None):
        if iterable is None:
            super().__init__()
        else:
            super().__init__(iterable)
        print('List initialized.')
>>> list = List
>>> a_list = list()
List initialized.
>>> type(a_list)
<class '__main__.List'>
del list
import builtins
builtins.list = List
>>> list_0 = list()
List initialized.
>>> type(list_0)
<class '__main__.List'>
>>> list_1 = []
>>> type(list_1)
<class 'list'>
>>> del builtins.list
>>> builtins.list
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: module 'builtins' has no attribute 'list'
>>> list()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'list' is not defined
>>> builtins.list = type([])
>>> list()
[]
call                 ::=  primary "(" [argument_list [","] | comprehension] ")"
>>> import dis
>>> dis.dis('list()')
  1           0 LOAD_NAME                0 (list)
              2 CALL_FUNCTION            0
              4 RETURN_VALUE
>>> dis.dis('doesnotexist()')
  1           0 LOAD_NAME                0 (doesnotexist)
              2 CALL_FUNCTION            0
              4 RETURN_VALUE
>>> dis.dis('[]')
  1           0 BUILD_LIST               0
              2 RETURN_VALUE