python exec()中的全局变量和本地变量

haw*_*ett 35 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
Run Code Online (Sandbox Code Playgroud)

这导致以下输出

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

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

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

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

locals: {'A': <class 'A'>}
A: <class 'A'>
{'A': <class 'A'>, 'B': <class 'B'>}
Run Code Online (Sandbox Code Playgroud)

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

然后很明显,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
Run Code Online (Sandbox Code Playgroud)

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

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

  class B(object):
    a_ref = A

f()

print 'Finished OK'
Run Code Online (Sandbox Code Playgroud)

*更新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
"""
Run Code Online (Sandbox Code Playgroud)

哪个输出

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

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

Roe*_*erg 18

嗯,我认为这是一个实现错误或无证的设计决定.问题的关键是模块范围中的名称绑定操作应该绑定到全局变量.它的实现方式是,在模块级别,globals()IS 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
Run Code Online (Sandbox Code Playgroud)

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

最后,它是CPython实现中的一个丑陋的黑客,特殊外壳全局查找.它还会导致一些荒谬的人为情况 - 例如:

>>> def f():
...     global a
...     a = 1
...
>>> f()
>>> 'a' in locals()
True
Run Code Online (Sandbox Code Playgroud)

请注意,这是我在阅读python语言参考的4.1节(命名和绑定)时弄乱解释器的所有推论.虽然这不是确定的(我没有打开CPython的来源),但我很确定我对这种行为是正确的.


小智 8

print locals()globals(),你会发现为什么高管抛出"没有定义"异常的原因,你可以试试这个

d = dict(locals(), **globals())
exec (code, d, d)
Run Code Online (Sandbox Code Playgroud)