我正在尝试使用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
所以我想新问题是 - 为什么这些本地人不会在函数和类定义中作为自由变量公开 - 这似乎是一个非常标准的闭包场景。
答案 0 :(得分: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
这种行为是继承工作的原因(名称查找使用了代码对象的范围locals(),其中确实有A)。
最后,它是CPython实现中的一个丑陋的黑客攻击,特殊外壳全局查找。它还会导致一些荒谬的人为情况 - 例如:
>>> def f():
... global a
... a = 1
...
>>> f()
>>> 'a' in locals()
True
请注意,这是基于在阅读python语言参考的第4.1节(命名和绑定)时弄乱解释器的所有推断。虽然这不是确定的(我没有打开CPython的来源),但我很确定我对这种行为是正确的。
答案 1 :(得分:7)
在print locals()
和globals()
之后,你会发现exec抛出“未定义”异常的原因,你可以试试这个
d = dict(locals(), **globals())
exec (code, d, d)
答案 2 :(得分:2)
如果你的问题是如何使exec
语句表现得像文件范围一样,我在链接的问题和错误中遵循了一些提示,并通过为全局和本地传递单个字典来实现它。显然,文件范围是一种特殊情况,其中本地声明自动放在全局范围内。
exec code in dict()
答案 3 :(得分:1)
my_code = """
class A(object):
pass
class B(object):
a = A
"""
my_code_AST = compile(my_code, "My Code", "exec")
extra_global = "hi"
global_env = {}
exec my_code_AST in global_env
print "global_env.keys() =", global_env.keys()
print "B.a =", global_env["B"].a
打印
global_env.keys() = ['__builtins__', 'A', 'B']
B.a = <class 'A'>
Hawkett,你说,
我想使用这样的本地人的主要原因是获取代码字符串中定义的所有内容,而没有python放入全局变量中的所有其他内容。
使用exec,如果你的全局变量没有定义__builtins__
,exec会在你的全局变量中添加一个__builtins__
项,所以你得到A,B和__builtins__
。 __builtins__
本身就是一个很大的字典,但它总是删除相同的一个元素(只要等到你的代码在删除它之前完成它的使用之后!)。记录在exec()here下。
built in functions下的eval文档说
如果全局字典存在且缺少“ builtins ”,则在解析表达式之前,会将当前全局变量复制到全局变量中。
但实际上似乎只是复制__builtins__
。
(并且n.b.其他所有人都说:要么设置全局变量和本地变量相同,要么说exec my_code_AST in global_env
没有单独的local_env。)