我正在尝试将字典子类化以用于exec方法。 最终,我希望本地函数具有自定义名称范围行为。
在下面的代码中,函数b()
确实具有正确的globals()
字典,但在解析z
时无法搜索它。
函数b()
首先不会搜索locals()
然后搜索globals()
吗?
非常令人费解。 任何帮助表示赞赏。
t = '''
def b():
# return (globals()['z']) #works
return z #fails
b()
'''
class MyDict(dict):
def __init__(self, g):
dict.__init__(self)
self.my_g = g
def __getitem__(self, key):
print("GET ", key)
try:
val = dict.__getitem__(self, key)
except:
print("GET exception1")
val = self.my_g[key]
return val
g = {'z':123}
md = MyDict(g)
#fails to find z
exec(t, md, md)
#works
#exec(t, g, g)
GET b
Traceback (most recent call last):
File "/project1/text12", line 31, in <module>
File "<string>", line 6, in <module>
File "<string>", line 4, in b
NameError: global name 'z' is not defined
答案 0 :(得分:4)
在python 3.3之前,不能使用自定义dict
子类作为exec语句的globals
值。执行编译代码的底层C代码直接访问底层C结构,忽略您可能实现的任何自定义钩子。
换句话说,当代码执行LOAD_GLOBAL
操作时(如函数z
中b
的情况),C字节码评估循环访问globals
使用C API在当前帧中构造,绕过任何python覆盖。
这在exec()
function documentation中记录为:
如果只提供 globals ,它必须是一个字典,它将用于全局变量和局部变量。如果给出 globals 和 locals ,则它们分别用于全局变量和局部变量。如果提供, locals 可以是任何映射对象。
Python 3.3中已放宽此限制,请参阅issue 14385。文档尚未更新,但字节码评估循环已更新,以便在回退到C API访问之前测试自定义映射。如果使用自定义映射,则使用PyObject_GetItem
函数,该函数将在自定义类上调用__getitem__
。