从正在运行的代码中,您可以轻松获取非局部变量的名称 - 但以调用的方式检索它们的内容locals
给你一本字典有点棘手。
使用过的nonlocal
变量名称存储在当前运行的代码对象的 co_freevars 属性中。
因此,获取非本地名称的问题是:
names = inspect.currentframe().f_code.co_freevars
The contents然而,这些变量存储在__closure__
属性 (func_closure
,在 Python 2) 中,函数对象反而。 (不是代码对象)。问题是,如果没有“外部的帮助”,正在运行的代码就没有简单的方法来访问它正在运行的函数对象。您可以访问框架对象,该对象链接到代码对象,但没有返回到函数对象的链接。 (对于顶级定义的函数,可以显式使用函数已知名称,如在def
语句`,但对于返回给调用者的封闭函数,也无法知道其名称)。
因此,人们必须诉诸一种技巧 - 通过使用 gc 模块(垃圾收集器)获取链接到当前代码对象的所有对象 - 有一个gc.get_referrers
call - 它将返回链接到所持有的代码对象的所有函数对象。
因此,在具有非局部变量的函数内可以执行以下操作:
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()
因此检索非本地人的名称和值。But如果您有多个该函数的实例(也就是说,如果感兴趣的函数通过多次调用生成它的函数多次创建),那么这将不起作用 - 因为所有这些实例都会链接到same代码对象。上面的示例假设只有一个函数使用当前代码运行 - 并且在这种情况下可以正常工作。对工厂函数的另一次调用将创建另一个函数,其中非局部变量可能具有其他值,但具有相同的代码对象 -function =
上面的列表生成器将检索所有这些,并任意选择其中的第一个。
“正确”的函数是当前代码正在执行的函数 - 我试图想出一种检索此信息的方法,但无法找到它。如果可以的话,我将完成这个答案,但目前,这无法帮助您检索非本地值。
update但是,只需将“eval”与非局部变量名一起使用即可。它只需显式声明为非本地,或者按嵌套函数中的名称进行硬编码,以便实际创建闭包。
def blah():
b = 1
def bleh():
nonlocals = sys._getframe().f_code.co_freevars
print(f"{eval(nonlocals[0])=}")
b # b has to be "touched" so the closure is created.
return ble
在发现简单的“eval”可以工作之前,我是这样离题的:
看起来,唯一将当前运行框架链接到保存非局部变量值的函数对象的东西是在运行时在 Python 解释器的本机端内创建的。我想不出除了使用 ctypes 模块在运行时查看解释器数据结构之外的方法,这当然不适合任何实际的生产代码。
底线:您可以可靠地检索非局部变量名称。但看起来您无法将其名称作为字符串获取它们的值(也无法重新绑定)。
您可以尝试在 Python 的错误跟踪器或 Python-ideas 邮件列表上打开“非本地”调用的功能请求。