当前位置: 首页 > 科技观察

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

时间:2023-03-16 00:01:27 科技观察

我们在日常使用Python的时候,经常需要创建一个列表。相信大家都不陌生吧?方法一:使用成对的方括号语法list_a=[]方法二:使用内置的list()listlist_b=list()以上两种写法你经常使用哪种?你想过他们的区别吗?开门见山,直接抛出本文的问题:[]和list()两种创建列表的写法,哪一种?更快,为什么更快?注:为了简化问题,我们以创建一个空列表为例进行分析。list的更多介绍和使用说明可以查看这篇文章1.[]比list()快三倍第一个问题,使用timeit模块的timeit()函数可以轻松计算:>>>importtimeit>>>timeit.timeit('[]',number=10**7)>>>timeit.timeit('list()',number=10**7)如上图所示,调用一个在千万次的情况下,[]创建方法只需要0.47秒,而list()创建方法需要1.75秒,所以后者是前者的3.7倍!这就回答了刚才的问题:对于空列表,create[]比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()的提速看完前两道题的答题过程,你可能会觉得不够过瘾,可能会觉得即使知道了这些冷知识也帮不上什么忙.似乎一点点的提升都是微不足道的。由于发散思维的习惯,我还想到了另一个很有意思的问题:list()的速度能不能提高?在刚刚发布的Python3.9.0版本中,实现了更快的vectorcall协议,因此执行速度会得到一定程度的提升。感兴趣的同学可以去Python官网下载3.9版本。根据我的多轮测试结果,新版本运行1000万次list()耗时约1秒,是[]的两倍,相比之前的近4倍数据,当前版本普遍提升了一个很多。