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函数
forLIST()
你应该只看恐怖:
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