当前位置: 首页 > 后端技术 > Python

Python困境:哪个更快,[]还是list()?为什么要快?快多少?

时间:2023-03-25 20:13:26 Python

本文来自《WhyPython》系列,请查看所有文章日常使用Python的时候,我们经常需要创建一个列表,相信大家都很熟练吧?#方法一:使用成对的方括号语法list_a=[]#方法二:使用内置的list()list_b=list()以上两种写法,你经常使用哪一种呢?你有想过它们之间的区别吗?开门见山,直接问本文的问题:[]和list()创建列表的两种写法哪种更快,为什么更快?注:为了简化问题,我们以创建一个空列表为例进行分析。list的更多介绍和使用说明可以查看这篇文章1.[]比list()快三倍第一个问题,使用timeit模块的timeit()函数很容易计算出来:>>>importtimeit>>>timeit.timeit('[]',number=10**7)>>>timeit.timeit('list()',number=10**7)如上图所示,在每个call1000万次的情况下,[]创建方法只需要0.47秒,而list()创建方法需要1.75秒,所以后者是前者的3.7倍!这就回答了刚才的问题:[]在创建空列表时比list()快很多。注意:timeit()函数的效率与运行环境有关,每次执行结果会略有不同。我在Python3.8中试验了几次,总的来说[]比list()快3倍多一点。2.list()比[]执行步骤多那么,我们继续分析第二个问题:为什么[]更快?这个时候我们可以使用dis模块的dis()函数来查看两者执行的字节码的区别:>>>fromdisimportdis>>>dis("[]")>>>dis("list()")如上图所示,[]的字节码有两条指令(BUILD_LIST和RETURN_VALUE),而list()的字节码有3条指令(LOAD_NAME、CALL_FUNCTION和RETURN_VALUE)。这些说明是什么意思?如何理解它们?首先,对于[]来说,它是Python中的一组字面量,和数字等字面量一样,表示精确的固定值。也就是说,Python在解析它的时候,知道它会代表一个列表,所以会直接调用解释器中构建列表的方法(对应BUILD_LIST)来创建一个列表,所以是一步到位过程。对于list()来说,“list”只是一个普通的名字,并不是字面值,也就是说解释器一开始并不能识别它。因此,解释器的第一步就是找到这个名字(对应于LOAD_NAME)。它会按照一定的顺序(局部作用域-全局作用域-内置作用域)在每个作用域中逐一搜索,直到找到为止,如果找不到,就会抛出NameError。解释器看到“list”后面有一对括号,所以第二步就是把这个名字当做可调用对象来调用,也就是当做函数来调用(对应CALL_FUNCTION)。所以list()创建列表时,需要经过namesearch和functioncall两步,才能真正开始创建list(注:CALL_FUNCTION底层会有一些函数调用流程,达到相同的逻辑作为BUILD_LIST。我们忽略的地方)。至此,我们就可以回答之前的问题了:因为list()的执行步骤多,所以比[]慢。3.list()的速度提高了。看完前两个问题的回答过程,你可能会觉得不够过瘾,可能会觉得即使知道了这些冰冷的知识也没有太大的帮助。似乎一点点的提升都是微不足道的。不过我们蟒猫出品的《Python为什么》系列一直秉承着孜孜不倦的求知精神,这个问题是不可能没有答案的。而且,由于发散思维的习惯,我还想到了另一个很有意思的问题:list()的速度能不能提高?我前不久写过一篇文章,正好讨论了这个问题,就是在刚刚发布的Python3.9.0版本中,对list()实现了更快的vectorcall协议,所以执行速度会得到一定程度的提升。感兴趣的同学可以去Python官网下载3.9版本。根据我的多轮测试结果,新版本运行1000万次list()耗时约1秒,是[]的两倍,相比之前的近4倍数据,当前版本普遍提升了一个很多。至此,我们已经回答了一系列问题。如果觉得自己有所收获,请点赞支持!欢迎大家关注后续更多精彩内容。本文属于“WhyPython”系列(Python猫出品),主要关注Python的语法、设计、开发等话题。它以每一个“为什么”的问题为切入点,试图展示Python的魅力。所有文章都会存档在Github上,欢迎大家给个小star,项目地址:https://github.com/chinesehua...