我正在尝试使用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
因此,我想一个新的问题是-为什么这些局部变量不会在函数和类定义中作为自由变量公开-似乎是一个非常标准的关闭方案。
好吧,我认为这可能是实现错误,也可能是未经证明的设计决策。问题的症结在于模块范围内的名称绑定操作应绑定到全局变量。实现的方式是,在模块级别,globals()是locals()(在解释器中尝试该值),因此,当您进行任何名称绑定时,它会像往常一样将其分配给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实现中的一个丑陋的骇客,专门用于对全局变量进行查找。它还会导致一些荒谬的人为情况-例如:
>>> def f(): ... global a ... a = 1 ... >>> f() >>> 'a' in locals() True
请注意,这是我在阅读python语言参考的第4.1节(命名和绑定)时基于与解释器混淆的所有推断。尽管这不是确定的(我还没有打开CPython的源代码),但我相当确定我对行为是正确的。