Python exec() 中的全局和局部变量
我正在尝试用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版本,感谢!
Colin
* 更新 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
所以我想新的问题是 - 为什么这些locals没有作为自由变量在函数和类定义中暴露出来 - 这看起来像是一个相当标准的闭包场景。
4 个回答
如果你在问怎么让 exec
这个语句像文件作用域那样工作,我参考了链接中的一些提示和问题,发现只需要传递一个字典给全局变量和局部变量就可以了。显然,文件作用域是一个特殊的情况,在这里局部声明会自动放到全局作用域中。
exec code in dict()
在你执行 print locals()
和 print globals()
之后,你会发现为什么 exec 会抛出“未定义”的异常。你可以试试看这个。
d = dict(locals(), **globals())
exec (code, d, d)
我觉得这可能是一个实现上的错误,或者是一个没有文档说明的设计决定。问题的关键在于,在模块范围内进行名称绑定操作时,应该绑定到一个全局变量。实现这个的方式是,当你在模块级别时,globals()和locals()是一样的(你可以在解释器里试试这个)。所以当你进行任何名称绑定时,它会像往常一样把它分配给locals()字典,而这个字典也是globals,因此就创建了一个全局变量。
当你查找一个变量时,首先会检查你当前的局部变量,如果找不到这个名字,就会递归地检查包含的作用域中的局部变量,直到找到这个变量或者到达模块范围。如果到达模块范围,就会检查全局变量,这些全局变量本应该是模块范围的局部变量。
>>> 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
这种行为就是为什么继承能够正常工作的原因(名称查找使用了代码对象的局部变量,而这个局部变量确实包含了A)。
总的来说,这在CPython的实现中是一个不太优雅的解决办法,特别处理了全局变量的查找。这也导致了一些不合逻辑的情况,比如:
>>> def f():
... global a
... a = 1
...
>>> f()
>>> 'a' in locals()
True
请注意,这些都是我在阅读Python语言参考的第4.1节(命名和绑定)时,玩弄解释器得出的推测。虽然这不是绝对的(我没有查看CPython的源代码),但我相当确定我对这种行为的理解是正确的。