python-3.x nonlocals()在哪里?

ni65a41a  于 2023-05-30  发布在  Python
关注(0)|答案(1)|浏览(214)

如何获取当前作用域的非局部变量?函数varslocalsglobals存在,但是否有一个函数可以获取nonlocals
为什么调用vars时没有列出非本地变量?

更新

我的问题是没有办法枚举当前作用域中可用的变量,因为varsglobals都不包括非局部变量AFAICT。
我经常在如下代码中使用vars

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

如果这些变量中的任何一个在包含函数的作用域中,则该函数将失败。

yb3bgrhw

yb3bgrhw1#

从运行的代码中,可以很容易地获得非局部变量的名称,但是通过调用locals获取字典来检索它们的内容有点麻烦。
使用的nonlocal变量名存储在当前运行的代码对象中的co_freevars属性中。
因此,获取非本地名称是一个问题:
names = inspect.currentframe().f_code.co_freevars
但是,这些变量的 contents 存储在 function object__closure__属性(在Python 2中为func_closure)中。(不是代码对象)。问题是,如果没有“来自外部的帮助”,运行的代码就没有简单的方法到达它所运行的函数对象。您可以访问框架对象,该对象链接到代码对象,但没有返回到函数对象的链接。(对于顶层定义的函数,可以显式地使用函数名,如def语句中所使用的,但对于返回给调用者的封闭函数,也没有办法知道它的名称)。 因此,我们不得不求助于一个技巧--通过使用gc模块(垃圾收集器)获取所有链接到当前代码对象的对象--有一个gc.get_referrers`调用--它将返回所有链接到我们持有的代码对象的函数对象。
因此,在一个带有非局部变量的函数中,我们可以这样做:

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()

因此检索非局部变量的名称和值。但是如果你有一个以上的函数示例(也就是说,如果感兴趣的函数被多次创建,并多次调用生成它的函数),这将不起作用-因为所有这些示例都将链接到同一个代码对象。上面的示例假设当前代码中只有一个函数在运行,并且在这种情况下可以正常工作。对factrory函数的另一次调用将创建另一个函数,其中可能包含非局部变量的其他值,但具有相同的代码对象-上面的function =列表生成器将检索所有这些值,并任意选择第一个值。
“正确”的函数是当前代码正在执行的函数--我试图想出一种检索此信息的方法,但无法获得它。如果可以的话,我将完成这个答案,但是现在,这不能帮助您检索非局部值。

update但是,只使用带有非局部变量名的“eval”就可以了。它只需要显式声明为nonlocal,或者在嵌套函数中通过名称硬编码,以便实际创建闭包。

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的bug跟踪器或Python-ideas邮件列表上打开一个“nonlocals”调用的特性请求。

相关问题