简单分析:为什么 [] 比 list() 快? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
chinesehuazhou
V2EX    Python

简单分析:为什么 [] 比 list() 快?

  •  
  •   chinesehuazhou 2020-10-14 22:22:11 +08:00 3297 次点击
    这是一个创建于 1873 天前的主题,其中的信息可能已经有所发展或是发生改变。

    分享一篇文章:

    本文出自“Python 为什么”系列,请查看全部文章

    在日常使用 Python 时,我们经常需要创建一个列表,相信大家都很熟练了吧?

    # 方法一:使用成对的方括号语法 list_a = [] # 方法二:使用内置的 list() list_b = list() 

    上面的两种写法,你经常使用哪一个呢?是否思考过它们的区别呢?

    让我们开门见山,直接抛出本文的问题吧:两种创建列表的 [] 与 list() 写法,哪一个更快呢,为什么它会更快呢?

    注:为了简化问题,我们以创建空列表为例进行分析。关于列表的更多介绍与用法说明,可以查看这篇文章

    1 、 [] 是 list() 的三倍快

    对于第一个问题,使用timeit模块的 timeit() 函数就能简单地测算出来:

    >>> import timeit >>> timeit.timeit('[]', number=10**7) >>> timeit.timeit('list()', number=10**7) 

    如上图所示,在各自调用一千万次的情况下,[] 创建方式只花费了 0.47 秒,而 list() 创建方式要花费 1.75 秒,所以,后者的耗时是前者的 3.7 倍!

    这就回答了刚才的问题:创建空列表时,[] 要比 list() 快不少。

    注:timeit() 函数的效率跟运行环境相关,每次执行结果会有微小差异。我在 Python3.8 版本实验了几次,总体上 [] 速度是 list() 的 3 倍多一点。

    2 、list() 比 [] 执行步骤多

    那么,我们继续来分析一下第二个问题:为什么 [] 会更快呢?

    这一次我们可以使用dis模块的 dis() 函数,看看两者执行的字节码有何差别:

    >>> from dis import dis >>> dis("[]") >>> dis("list()") 

    如上图所示,[] 的字节码有两条指令( BUILD_LIST 与 RETURN_VALUE ),而 list() 的字节码有三条指令( LOAD_NAME 、CALL_FUNCTION 与 RETURN_VALUE )。

    这些指令意味着什么呢?该如何理解它们呢?

    首先,对于 [],它是 Python 中的一组字面量( literal ),像数字之类的字面量一样,表示确切的固定值。

    也就是说,Python 在解析到它时,就知道它要表示一个列表,因此会直接调用解释器中构建列表的方法(对应 BUILD_LIST ),来创建列表,所以是一步到位。

    而对于 list(),“list”只是一个普通的名称,并不是字面量,也就是说解释器一开始并不认识它。

    因此,解释器的第一步是要找到这个名称(对应 LOAD_NAME )。它会按照一定的顺序,在各个作用域中逐一查找(局部作用域--全局作用域--内置作用域),直到找到为止,找不到则会抛出NameError

    解释器看到“list”之后是一对圆括号,因此第二步是把这个名称当作可调用对象来调用,即把它当成一个函数进行调用(对应 CALL_FUNCTION )。

    因此,list() 在创建列表时,需要经过名称查找与函数调用两个步骤,才能真正开始创建列表(注:CALL_FUNCTION 在底层还会有一些函数调用过程,才能走到跟 BUILD_LIST 相通的逻辑,此处我们忽略不计)。

    至此,我们就可以回答前面的问题了:因为 list() 涉及的执行步骤更多,因此它比 [] 要慢一些。

    3 、list() 的速度提升

    看完前两个问题的解答过程,你也许觉得还不够过瘾,而且可能觉得就算知道了这个冷知识,也不会有多大的帮助,似乎那微弱的提升显得微不足道。

    但是,我们Python 猫出品的《Python 为什么》系列一直秉承着孜孜不倦的求知精神,是不可能放着这个问题不去回答的。

    而且,由于有发散性思考的习惯,我还想到了另外一个挺有意思的问题:list() 的速度能否提升呢?

    我不久前写过一篇文章 正好讨论到这个问题,也就是在刚刚发布的 Python 3.9.0 版本中,它给 list() 实现了更快的 vectorcall 协议,因此执行速度会有一定的提升。

    感兴趣的同学可以去 Python 官网下载 3.9 版本。

    根据我多轮的测试结果,在新版本中运行 list() 一千万次,耗时大概在 1 秒左右,也就是 [] 运行耗时的 2 倍,相比于前面接近 4 倍的数据,当前版本总体上是提升了不少。

    至此,我们已回答完一连串的疑问,如果你觉得有收获,请点赞支持!欢迎大家关注后续更多精彩内容。

    本文属于“Python 为什么”系列( Python 猫出品),该系列主要关注 Python 的语法、设计和发展等话题,以一个个“为什么”式的问题为切入点,试着展现 Python 的迷人魅力。所有文章将会归档在 Github 上,欢迎大家给颗小星星,项目地址: https://github.com/chinesehuazhou/python-whydo

    12 条回复    2020-10-15 20:03:20 +08:00
    love
        1
    love  
       2020-10-14 23:06:04 +08:00   1
    没用的知识又学到了一点
    black11black
        2
    black11black  
       2020-10-15 01:52:22 +08:00
    实际代码里都是 O(1),慢一千倍也照样用。倒是个人感觉 list()可读性要好些,所以不爱写[]
    luckyc
        3
    luckyc  
       2020-10-15 08:51:12 +08:00
    一直偷懒写[] 没想到蒙对了.
    chinesehuazhou
        4
    chinesehuazhou  
    OP
       2020-10-15 08:53:42 +08:00 via Android
    @black11black 这里快慢不影响选择。不过我个人相反,觉得 [] 更可读呢
    julyclyde
        5
    julyclyde  
       2020-10-15 11:52:44 +08:00
    只不过是 python 本身没做好,居然变成外围考据党的写作起点了
    这和无病呻吟的红楼梦学有什么区别
    chinesehuazhou
        6
    chinesehuazhou  
    OP
       2020-10-15 12:30:33 +08:00 via Android
    @julyclyde 那我可得好好感谢 Python
    yucongo
        7
    yucongo  
       2020-10-15 14:01:17 +08:00   1
    In [110]: %timeit [*range(100000)]
    12.9 ms ± 446 s per loop (mean ± std. dev. of 7 runs, 100 loops each)

    In [111]: %timeit list(range(100000))
    12.9 ms ± 343 s per loop (mean ± std. dev. of 7 runs, 100 loops each)

    貌似基本没啥区别,有时 list 还快些
    scottyeung
        8
    scottyeung  
       2020-10-15 14:32:17 +08:00
    自己跑了一下确实是
    学到了
    lusi1990
        9
    lusi1990  
       2020-10-15 15:07:38 +08:00
    @yucongo 如果这样初始化, list() 确实会更快一些.
    catsoul
        10
    catsoul  
       2020-10-15 15:43:27 +08:00
    @yucongo 这种东西没啥意义,基本上就跟各种电子产品上写的“实验室测试环境下续航 xx 小时”一样没有任何参考意义。试问,谁会没事儿一波子创建上千万个空列表呢?
    yucongo
        11
    yucongo  
       2020-10-15 16:43:46 +08:00
    @catsoul 这个不是千万个空表,而是从生成器生成表,是有实用场景的
    secondwtq
        12
    secondwtq  
       2020-10-15 20:03:20 +08:00
    这个 vectorcall 和 fastcall,不看 PEP 还以为是 x86 ABI 的那东西 ...
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     3199 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 23ms UTC 11:36 PVG 19:36 LAX 03:36 JFK 06:36
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86