globals()vs locals()可变性

lum*_*ota 4 python state mutability python-internals

在Python中,globals()返回全局符号表locals()的表示,同时返回本地状态的表示.虽然两者都返回字典,但更改将globals()在全局符号表中生效,而更改locals()则无效.

为什么会这样?

Mar*_*ers 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        
Run Code Online (Sandbox Code Playgroud)

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        
Run Code Online (Sandbox Code Playgroud)

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

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

  • @lumisota:因为globals是a)分层(它也到达内置)并且必须支持在运行时改变(使用`global`关键字,以及在运行时添加到模块命名空间). (2认同)