4

在 Python 中,globals()返回全局符号表的表示,同时locals()返回局部状态的表示。虽然两者都返回字典,但更改globals()会在全局符号表中生效,而更改locals()不会生效。

为什么会这样?

4

1 回答 1

7

函数局部变量在编译时经过高度优化和确定,CPython 建立在无法在运行时动态更改已知局部变量的基础上。

您可以在解码函数字节码时看到这一点:

>>> import dis
>>> def foo():
...     a = 'bar'
...     return a + 'baz'
... 
>>> dis.dis(foo)
  2           0 LOAD_CONST               1 ('bar')
              3 STORE_FAST               0 (a)

  3           6 LOAD_FAST                0 (a)
              9 LOAD_CONST               2 ('baz')
             12 BINARY_ADD          
             13 RETURN_VALUE        

LOAD_FASTSTORE_FAST操作码使用索引来加载和存储变量,因为在帧上,局部变量被实现为数组。访问数组比使用哈希表(字典)更快,例如用于全局命名空间。

locals()函数在函数中使用时,会将该数组的反射作为字典返回。更改locals()字典不会将其反映回数组中。

在 Python 2 中,如果您exec在代码中使用该语句,则优化(部分)被破坏;在这种情况下, Python 使用较慢的LOAD_NAME操作码

>>> def bar(code):
...     exec code
...     return a + 'baz'
... 
>>> dis.dis(bar)
  2           0 LOAD_FAST                0 (code)
              3 LOAD_CONST               0 (None)
              6 DUP_TOP             
              7 EXEC_STMT           

  3           8 LOAD_NAME                0 (a)
             11 LOAD_CONST               1 ('baz')
             14 BINARY_ADD          
             15 RETURN_VALUE        

另请参阅此针对 Python 3 的错误报告,其中exec()(Py3 中的一个函数)不再允许您设置本地名称:

动态修改函数的局部变量是不可能的,没有几个后果:通常,函数局部变量不存储在字典中,而是存储在数组中,其索引是在编译时从已知的语言环境中确定的。这至少与 exec 添加的新本地人发生冲突。 旧的 exec 语句规避了这一点,因为编译器知道,如果函数中出现没有全局/局部参数的 exec,则该命名空间将是“未优化的”,即不使用 locals 数组。 由于 exec() 现在是一个普通函数,编译器不知道“exec”可能绑定到什么,因此不能特殊处理。

于 2015-03-16T17:54:21.643 回答