• 关注官方微信 微信公众号 添加方式:
    1:搜索微信号(gogolinux
    2:扫描左侧二维码
  • 登录 注册
  • 一起学LINUX - GOGOLINUX

    查看: 375|回复: 0
    打印 上一主题 下一主题

    Python中,代码放在函数中运行为什么比放在全局中运行快? ...

    [复制链接]

    1

    主题

    1

    帖子

    20

    积分

    新手上路

    Rank: 1

    积分
    20
    跳转到指定楼层
    楼主
    发表于 2019-6-5 14:33:03 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
    因为CPython的解释器实现细节。
    CPython的解释器,对局部变量是用数组存储,用下标来访问;而对全局变量是用dict来存储,用符号(symbol)来做hash访问。速度差距是杠杠的。
    这是因为一旦函数定义好之后,局部变量的个数就不能改了,所以可以用固定大小的容器存储;而全局名字是可以一边执行一边改变的,所以得用更动态的方式来存储。

    看CPython的字节码的 LOAD_FAST 与 LOAD_GLOBAL 就可以看出差异了 ^_^

    对上面的描述觉得迷惑的同学,可以先参考一些背景资料:有没有内容类似于《Python源码剖析》,但内容更新过,针对新版本的Python书籍? - RednaxelaFX 的回答
    特别是其中用Python实现的教学用CPython字节码解释器的例子:
    A Python Interpreter Written in Python(其代码在 byterun/pyvm2.py at master · nedbat/byterun · GitHub
    这个用Python实现的解释器其中就有这里提到的读写局部变量用的 LOAD_FAST / STORE_FAST 指令的实现——但为了简单起见,它实现这两条字节码指令是用 dict 来存储数据的,而不是像真正的CPython那样用数组来存储。其实稍微改改这个Python代码就可以让它在这方面更接近CPython的样子了。

    ===========================================

    评论区有同学提到 locals(),这在正常Python里是改变不了实际局部变量的状态的喔:
    $ python Python 2.7.5 (default, Mar  9 2014, 22:15:05)  [GCC 4.2.1 Compatible Apple LLVM 5.0 (clang-500.0.68)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> def foo(): ...   a = 1 ...   b = 2 ...   locals()['a'] = 42 ...   print(a, b) ...  >>> foo() (1, 2) >>> import dis >>> dis.dis(foo)   2           0 LOAD_CONST               1 (1)               3 STORE_FAST               0 (a)    3           6 LOAD_CONST               2 (2)               9 STORE_FAST               1 (b)    4          12 LOAD_CONST               3 (42)              15 LOAD_GLOBAL              0 (locals)              18 CALL_FUNCTION            0              21 LOAD_CONST               4 ('a')              24 STORE_SUBSCR            5          25 LOAD_FAST                0 (a)              28 LOAD_FAST                1 (b)              31 BUILD_TUPLE              2              34 PRINT_ITEM                        35 PRINT_NEWLINE                     36 LOAD_CONST               0 (None)              39 RETURN_VALUE

    然后也有提到exec / eval的:
    >>> def bar(): ...   a = 1 ...   b = 2 ...   exec('c = 3') ...   print(a, b, c) ...   print(locals()) ...  >>> bar() (1, 2, 3) {'a': 1, 'c': 3, 'b': 2} >>> dis.dis(bar)   2           0 LOAD_CONST               1 (1)               3 STORE_FAST               0 (a)    3           6 LOAD_CONST               2 (2)               9 STORE_FAST               1 (b)    4          12 LOAD_CONST               3 ('c = 3')              15 LOAD_CONST               0 (None)              18 DUP_TOP                           19 EXEC_STMT               5          20 LOAD_FAST                0 (a)              23 LOAD_FAST                1 (b)              26 LOAD_NAME                0 (c)              29 BUILD_TUPLE              3              32 PRINT_ITEM                        33 PRINT_NEWLINE           6          34 LOAD_NAME                1 (locals)              37 CALL_FUNCTION            0              40 PRINT_ITEM                        41 PRINT_NEWLINE                     42 LOAD_CONST               0 (None)              45 RETURN_VALUE
    注意这里“局部变量”c是在一个exec语句(Python 3的话是exec()函数)里动态定义的,而在exec语句后对c的使用就用的是LOAD_NAME字节码而不是普通局部变量用的LOAD_FAST——这说明了动态定义的局部变量与普通局部变量的差异,而前面说“局部变量的个数不会改变”不包括这种动态定义的情况。
    分享到:
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    官方微博:

    官方头条号:

    官方微信

    手机访问:

    官方微信

    QQArchiver 手机版 小黑屋 一起学LINUX - GOGOLINUX 闽ICP备18025837号-1 Discuz! X3.4 Powered by © 2001-2013 Comsenz Inc. 

    本站资源均来自互联网或会员发布,如果侵犯了您的权益请与我们联系,我们将在24小时内删除!谢谢!

    快速回复 快速发帖 返回顶部 返回列表