globals() vs locals() mutability

孤人 提交于 2019-11-30 10:19:21

Function locals are highly optimised and determined at compile time, CPython builds on not being able to alter the known locals dynamically at runtime.

You can see this when decoding a function bytecode:

>>> 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        

The LOAD_FAST and STORE_FAST opcodes use indices to load and store variables, because on a frame the locals is implemented as an array. Access to an array is faster than using a hash table (dictionary), such as used for the global namespace.

The locals() function, when used in a function, then returns a reflection of this array as a dictionary. Altering the locals() dictionary won't then reflect that back into the array.

In Python 2, if you use the exec statement in your code then the optimisation is (partly) broken; Python uses the slower LOAD_NAME opcode in that case:

>>> 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        

Also see this bug report against Python 3 where exec() (a function in Py3) doesn't allow you to set local names anymore:

To modify the locals of a function on the fly is not possible without several consequences: normally, function locals are not stored in a dictionary, but an array, whose indices are determined at compile time from the known locales. This collides at least with new locals added by exec. The old exec statement circumvented this, because the compiler knew that if an exec without globals/locals args occurred in a function, that namespace would be "unoptimized", i.e. not using the locals array. Since exec() is now a normal function, the compiler does not know what "exec" may be bound to, and therefore can not treat is specially.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!