如何确定Python堆栈框架可见的命名空间?

时间:2013-01-04 13:36:58

标签: python metaprogramming

请考虑以下代码:

GLOBAL_VARIABLE = 1

def someFunction():
    nonLocalVariable = 2

    def anotherFunction():
        localVariable = 3

        class LocalClass(object):
            __metaclass__ = MyMetaClass
            __customCode = """
                    GLOBAL_VARIABLE + nonLocalVariable + localVariable
                """

    anotherFunction()

someFunction()

我是MyMetaClass的实现者,这是一个为其生成方法的元类 基于__customCode属性内容的类。 __customCode 可以包含Python表达式,所以我想确定一个变量名 __customCode中提到的同一个对象与同一个变量名称相同 用普通的Python方法定义。

当调用元类时,它会传递一个包含内容的字典 这个类,这意味着它知道__customCode,但这没有多大帮助。 如果您使用inspect.currentframe(1),则会获得class所在的堆栈框架 语句正在执行,也就是说anotherFunction()。那个堆栈框架 有一个.f_locals属性(包含localVariable)和一个.f_globals 属性(包含GLOBAL_VARIABLE,除其他外),但这两个 一起不讲述整个故事。

给定anotherFunction()的堆栈帧(或者我能抓住的任何其他东西) 从MyMetaClass的实现中,我怎么才能发现 nonLocalVariable所在的命名空间,以及嵌套的任何其他命名空间 全局命名空间和本地命名空间之间?

我目前正在使用Python 2.7,但如果Python 3.x中的答案不同, 那也很好。

1 个答案:

答案 0 :(得分:4)

除了locals()globals()结构之外,编译器还会在嵌套函数中以单元格的形式添加关于“自由变量”的信息,对于任何 local a)未被赋值的变量,b)引用范围变量(Python 3.x添加nonlocal关键字以允许您分配给这样的变量,就像{{1}一样}关键字)。

在您的具体情况下,没有这样的变量。您的嵌套代码均未引用global;只有当nonLocalVariableanotherFunction实际使用该变量时,python编译器(因此在字节编译时)添加必要的结构才能从范围中提取LocalClass

以下是嵌套设置示例:

nonLocalVariable

请注意>>> def foo(x): ... y = None ... def bar(): ... return x ... return bar ... >>> bar = foo('spam') >>> foo.__code__.co_cellvars ('x',) >>> bar.__code__.co_freevars ('x',) >>> dir(bar.func_closure[0]) ['__class__', '__cmp__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'cell_contents'] >>> bar.func_closure[0].cell_contents 'spam' 列表中的foo代码对象列表x以及co_cellvarsbar的代码对象x列出co_freevars由于y从不使用bar,因此<{1}} 未列出 。对bar函数的引用具有func_closure结构,该结构保存x当前值,这是bar创建时范围所给出的值。代码对象保存函数的字节码;它们是在编译时创建的。 func_closure元组是在运行时根据freevarscellvars结构创建的。

因为这在编译时发生,所以动态创建对这样的范围变量的引用充其量是棘手的。为了您自己的利益,在这种情况下,我甚至不愿意支持使用范围变量。