python - python exec() 中的全局变量和局部变量

标签 python scope

我正在尝试使用 exec 运行一段 python 代码。

my_code = """
class A(object):
  pass

print 'locals: %s' % locals()
print 'A: %s' % A

class B(object):
  a_ref = A
"""

global_env = {}
local_env = {}
my_code_AST = compile(my_code, "My Code", "exec")
exec(my_code_AST, global_env, local_env)

print local_env

导致以下输出

locals: {'A': <class 'A'>}
A: <class 'A'>
Traceback (most recent call last):
  File "python_test.py", line 16, in <module>
    exec(my_code_AST, global_env, local_env)
  File "My Code", line 8, in <module>
  File "My Code", line 9, in B
NameError: name 'A' is not defined

但是,如果我将代码更改为此 -

my_code = """
class A(object):
  pass

print 'locals: %s' % locals()
print 'A: %s' % A

class B(A):
  pass
"""

global_env = {}
local_env = {}
my_code_AST = compile(my_code, "My Code", "exec")
exec(my_code_AST, global_env, local_env)

print local_env

然后它工作正常 - 给出以下输出 -

locals: {'A': <class 'A'>}
A: <class 'A'>
{'A': <class 'A'>, 'B': <class 'B'>}

显然 A 存在且可访问 - 第一段代码出了什么问题?我正在使用 2.6.5,干杯,

科林

* 更新 1 *

如果我检查类中的 locals() -

my_code = """
class A(object):
  pass

print 'locals: %s' % locals()
print 'A: %s' % A

class B(object):
  print locals()
  a_ref = A
"""

global_env = {}
local_env = {}
my_code_AST = compile(my_code, "My Code", "exec")
exec(my_code_AST, global_env, local_env)

print local_env

那么很明显locals()在两个地方都不一样-

locals: {'A': <class 'A'>}
A: <class 'A'>
{'__module__': '__builtin__'}
Traceback (most recent call last):
  File "python_test.py", line 16, in <module>
    exec(my_code_AST, global_env, local_env)
  File "My Code", line 8, in <module>
  File "My Code", line 10, in B
NameError: name 'A' is not defined

但是,如果我这样做,没有问题-

def f():
  class A(object):
    pass

  class B(object):
    a_ref = A

f()

print 'Finished OK'

* 更新 2 *

好的,这里的文档 - http://docs.python.org/reference/executionmodel.html

'类定义是可以使用和定义名称的可执行语句。这些引用遵循名称解析的正常规则。类定义的命名空间成为类的属性字典。在类范围内定义的名称在方法中不可见。'

在我看来,'A' 应该在作为 B 的定义的可执行语句中作为自由变量使用,当我们调用上面的 f() 时会发生这种情况,但当我们使用 exec() 时不会发生这种情况。这可以通过以下方式更容易地显示 -

my_code = """
class A(object):
  pass

print 'locals in body: %s' % locals()
print 'A: %s' % A

def f():
  print 'A in f: %s' % A

f()

class B(object):
  a_ref = A
"""

哪个输出

locals in body: {'A': <class 'A'>}
A: <class 'A'>
Traceback (most recent call last):
  File "python_test.py", line 20, in <module>
    exec(my_code_AST, global_env, local_env)
  File "My Code", line 11, in <module>
  File "My Code", line 9, in f
NameError: global name 'A' is not defined

所以我猜新问题是 - 为什么这些本地变量不作为函数和类定义中的自由变量公开 - 这似乎是一个非常标准的闭包场景。

最佳答案

好吧,我相信这要么是一个实现错误,要么是一个未记录的设计决策。问题的症结在于模块范围内的名称绑定(bind)操作应该绑定(bind)到全局变量。实现的方法是,在模块级别时, globals() 是 locals() (在解释器中尝试那个),所以当你做任何名称绑定(bind)时,它像往常一样将它分配给 locals( ) 字典,它也是全局变量,因此创建了一个全局变量。

查找变量时,首先检查当前的局部变量,如果找不到名称,则递归检查变量名称的包含范围的局部变量,直到找到变量或到达模块范围。如果你达到了,你检查全局变量,它们应该是模块作用域的局部变量。

>>> exec(compile("import sys\nprint sys._getframe().f_code.co_name", "blah", "exec"), {}, {})
<module>
>>> exec("a = 1\nclass A(object):\n\tprint a\n", {}, {})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 2, in <module>
  File "<string>", line 3, in A
NameError: name 'a' is not defined
>>> d = {}
>>> exec("a = 1\nclass A(object):\n\tprint a\n", d,d)
1

这种行为是继承起作用的原因(名称查找使用代码对象的范围 locals(),其中确实包含 A)。

最后,这是 CPython 实现中的一个丑陋的 hack,特殊情况下的全局查找。它还会导致一些荒谬的人为情况 - 例如:

>>> def f():
...     global a
...     a = 1
...
>>> f()
>>> 'a' in locals()
True

请注意,这是我在阅读 Python 语言引用的第 4.1 节(命名和绑定(bind))时弄乱解释器的所有推论。虽然这不是确定的(我还没有打开 CPython 的源代码),但我相当确定我的行为是正确的。

关于python - python exec() 中的全局变量和局部变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2904274/

相关文章:

python - 将生成器附加到循环中的堆栈,生成器指向最终循环变量

c++ - 使用 C header 时如何解决名称冲突?

objective-c - ObjC : Local variable seems to retain its value across function calls

python - 在Python中将变量保存为不带括号的列

python - 句子打印太多次

scope - Lua - 函数中的局部变量作用域

c++ - 什么时候在 C++ 中调用复制构造函数? - 函数返回

python - 如何仅将 'Unnamed:' 列重命名为数字索引

python - 在python中追加错误

Python 将列表值转换为字符串