3

我正在尝试将字典子类化以在 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
4

1 回答 1

5

在 python 3.3 之前,您不能将自定义dict子类用于globalsexec 语句的值。执行编译代码的底层 C 代码直接访问底层 C 结构,忽略您可能已经实现的任何自定义挂钩。

换句话说,当代码执行LOAD_GLOBAL操作时(就像z在你的函数中的情况一样b),C 字节码评估循环使用C APIglobals访问当前帧中的结构,绕过任何 python 覆盖。

这在exec()功能文档中记录为:

如果只提供全局变量,它必须是一个字典,它将用于全局变量和局部变量。如果给出了全局变量和局部变量,则它们分别用于全局变量和局部变量。如果提供,locals可以是任何映射对象。

Python 3.3 中放宽了此限制,请参阅issue 14385。文档尚未更新,但字节码评估循环已更新,可在回退到 C API 访问之前测试自定义映射。如果使用自定义映射,则使用该函数,该PyObject_GetItem函数将调用__getitem__自定义类。

于 2012-08-29T19:39:52.210 回答