exec() 方法中自定义命名空间的子类化 python 字典

2022-01-14 00:00:00 python dictionary exec namespaces subclass

问题描述

我正在尝试对字典进行子类化以在 exec 方法中使用.最终,我希望本地函数具有自定义名称范围行为.

I am trying to subclass a dictionary for use in an exec method. Ultimately, I would like the local function to have a custom name scoping behaviour.

在下面的代码中,函数 b() 实际上确实有正确的 globals() 字典可供它使用,但是在解析 z.

In the code below, function b() does in fact have the correct globals() dictionary available to it, however it fails to search it when resolving z.

函数b()是否先不搜索locals()然后globals()?

Does function b() first not search locals() then globals() ?

非常令人费解.任何帮助表示赞赏.

Very puzzling. Any help appreciated.

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


解决方案

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

Before python 3.3, you cannot use a custom dict subclass for the globals value of an exec statement. The underlying C code that executes the compiled code accesses the underlying C structures directly, ignoring any custom hooks you may have implemented.

换句话说,当代码执行 LOAD_GLOBAL 操作时(就像函数 b 中的 z 一样),C字节码评估循环使用 globals 结构">C API,绕过任何 python 覆盖.

In other words, when the code does a LOAD_GLOBAL operation (as is the case with z in your function b), the C bytecode evaluation loop accesses the globals structure in the current frame using the C API, bypassing any python overrides.

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

This is documented in the exec() function documentation as:

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

If only globals is provided, it must be a dictionary, which will be used for both the global and the local variables. If globals and locals are given, they are used for the global and local variables, respectively. If provided, locals can be any mapping object.

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

This restriction has been loosened in Python 3.3, see issue 14385. The documentation hasn't been updated yet, but the bytecode evaluation loop has been updated to test for custom mappings before falling back to the C API access. If a custom mapping is used, the PyObject_GetItem function is used, which will call __getitem__ on custom classes.

相关文章