5

如何获取当前范围的非局部变量?函数vars,localsglobals存在,但是有没有函数可以得到nonlocals

为什么调用时没有列出非本地人vars

更新

我的问题是无法枚举当前范围内可用的变量,因为既不包括vars也不globals包括非本地 AFAICT。

我经常vars在如下代码中使用:

'{meh[0]}/{meh[3]} {a}{b}{c}'.format(**vars())

如果这些变量中的任何一个在包含函数的范围内,则会失败。

4

1 回答 1

9

从正在运行的代码中,您可以轻松获取非局部变量的名称 - 但是以调用locals获取字典的方式检索它们的内容有点棘手。

使用的nonlocal变量名称存储在当前运行的代码对象的 co_freevars 属性中。

因此,获取非本地名称是一个问题:

names = inspect.currentframe().f_code.co_freevars

但是,这些变量的内容存储在函数对象__closure__的属性(func_closure在 Python 2 中)中。(不是代码对象)。问题是,如果没有“来自外部的帮助”,运行代码就没有简单的方法可以访问它正在运行的函数对象。您可以访问框架对象,该对象链接到代码对象,但没有返回到函数对象的链接。(对于顶级定义的函数,可以显式使用函数已知名称,如语句中使用的那样,但对于返回给调用者的封闭函数,也无法知道其名称)。def

因此,必须求助于一个技巧——通过使用 gc 模块(垃圾收集器)获取链接到当前代码对象的所有对象——有一个gc.get_referrers调用——它将返回链接到代码对象的所有函数对象一个持有。

因此,在具有 non_local 变量的函数中,可以执行以下操作:

import inspect, gc

from types import FunctionType

def a(b):
    b1 = 2
    def c():
        nonlocal b1
        print (b)
        code =  inspect.currentframe().f_code  
        names = code.co_freevars
        function = [func for func in gc.get_referrers(code) if isinstance(func, FunctionType)][0]
        nonlocals = dict (zip(names, (x.cell_contents for x in function.__closure__ )))
        print(nonlocals)
        return inspect.currentframe()
    return c

c = a(5)
f = c()

因此检索非本地人的名称和值。但是,如果您周围有多个该函数的实例(即,如果感兴趣的函数被多次创建并多次调用生成它的函数),这将不起作用 - 因为所有这些实例都会链接到相同的代码对象。上面的示例假设只有一个函数与当前代码一起运行——并且在这种情况下可以正常工作。对工厂函数的另一个调用将创建另一个函数,可能具有非局部变量的其他值,但具有相同的代码对象 -function = 上面的列表生成器将检索所有这些,并任意选择其中的第一个。

“正确”函数是当前代码正在执行的函数——我试图想出一种检索此信息的方法,但无法得到它。如果可以,我会完成这个答案,但现在,这不能帮助您检索非本地值值。

(刚刚发现尝试使用带有非局部变量名的“eval”也行不通)

看起来,将当前运行的帧链接到保存非局部变量值的函数对象的唯一方法是在运行时在 Python 解释器的本机端内创建的。除了使用 ctypes 模块在运行时查看解释器数据结构之外,我想不出一种方法,这当然不适合任何实际的生产代码。

底线:您可以可靠地检索非局部变量名称。但看起来你不能得到他们的价值,因为他们的名字是一个字符串(也不能重新绑定)。

您可以尝试在 Python 的错误跟踪器或 Python-ideas 邮件列表上打开“非本地人”调用的功能请求。

于 2012-01-23T11:07:54.153 回答