解析仅给定堆栈帧对象的变量名称
Resolve a variable name given only a stack frame object
我正在尝试找出是否可以解析堆栈帧中的变量(由 inspect.currentframe()
返回)。
换句话说,我正在寻找一个函数
def resolve_variable(variable_name, frame_object):
return value_of_that_variable_in_that_stackframe
例如,请考虑以下代码:
global_var = 'global'
def foo():
closure_var = 'closure'
def bar(param):
local_var = 'local'
frame = inspect.currentframe()
assert resolve_variable('local_var', frame) == local_var
assert resolve_variable('param', frame) == param
assert resolve_variable('closure_var', frame) == closure_var
assert resolve_variable('global_var', frame) == global_var
bar('parameter')
foo()
通过框架对象的 f_locals
和 f_globals
属性简单地查找局部和全局变量:
def resolve_variable(variable_name, frame_object):
try:
return frame_object.f_locals[variable_name]
except KeyError:
try:
return frame_object.f_globals[variable_name]
except KeyError:
raise NameError(varname) from None
但问题是闭包变量。据我所知,它们不像局部变量和全局变量那样存储在字典中。更糟糕的是,变量只有在函数实际访问它们时才会成为闭包变量(例如通过读取它的值 _ = closure_var
或使用 nonlocal closure_var; closure_var = _
写入它)。所以实际上有 3 种不同的情况:
global_var = 'global'
def foo():
unused_cvar = 'unused' # actually not a closure variable at all
readonly_cvar = 'closure'
nonlocal_cvar = 'nonlocal'
def bar(param):
nonlocal nonlocal_cvar
local_var = 'local'
_ = readonly_cvar
nonlocal_cvar = 'nonlocal'
frame = inspect.currentframe()
assert resolve_variable('local_var', frame) == local_var
assert resolve_variable('param', frame) == param
assert resolve_variable('unused_cvar', frame) == 'unused'
assert resolve_variable('readonly_cvar', frame) == readonly_cvar
assert resolve_variable('nonlocal_cvar', frame) == nonlocal_cvar
assert resolve_variable('global_var', frame) == global_var
bar('parameter')
foo()
如何重写我的 resolve_variable
函数以支持所有这些?有可能吗?
一般不可能。 Python 只保留闭包实际引用的闭包变量。
>>> import inspect
>>> class Demo(object):
... def __del__(self):
... print("Too late, it's gone.")
...
>>> def f():
... a = Demo()
... def g():
... return inspect.currentframe()
... return g
...
>>> frame = f()()
Too late, it's gone.
从这个例子可以看出,从框架 frame
中检查 a
是不可能的。没了。
至于框架实际使用的闭包变量,那些通常出现在f_locals
中。我知道一个奇怪的情况,他们不会,如果框架是针对带有闭包变量的 class 语句:
>>> def f():
... a = 1
... class Foo(object):
... print(a)
... print(inspect.currentframe().f_locals)
... return Foo
...
>>> f()
1
{'__module__': '__main__', '__qualname__': 'f.<locals>.Foo'}
<class '__main__.f.<locals>.Foo'>
在深入了解 CPython 实现后(特别是框架对象、LOAD_CLASSDEREF
操作码和 inspect.getclosurevars
),我认为访问 class 框架的唯一方法闭包变量将使用 ctypes
、gc.get_referents
或类似的令人讨厌的方法。
此外,请注意,如果闭包变量值在访问后发生变化,则 f_locals
字典可能不是最新的;再次访问 frame.f_locals
refreshes the contents,但在您查看时它可能已经过时了。
我正在尝试找出是否可以解析堆栈帧中的变量(由 inspect.currentframe()
返回)。
换句话说,我正在寻找一个函数
def resolve_variable(variable_name, frame_object):
return value_of_that_variable_in_that_stackframe
例如,请考虑以下代码:
global_var = 'global'
def foo():
closure_var = 'closure'
def bar(param):
local_var = 'local'
frame = inspect.currentframe()
assert resolve_variable('local_var', frame) == local_var
assert resolve_variable('param', frame) == param
assert resolve_variable('closure_var', frame) == closure_var
assert resolve_variable('global_var', frame) == global_var
bar('parameter')
foo()
通过框架对象的 f_locals
和 f_globals
属性简单地查找局部和全局变量:
def resolve_variable(variable_name, frame_object):
try:
return frame_object.f_locals[variable_name]
except KeyError:
try:
return frame_object.f_globals[variable_name]
except KeyError:
raise NameError(varname) from None
但问题是闭包变量。据我所知,它们不像局部变量和全局变量那样存储在字典中。更糟糕的是,变量只有在函数实际访问它们时才会成为闭包变量(例如通过读取它的值 _ = closure_var
或使用 nonlocal closure_var; closure_var = _
写入它)。所以实际上有 3 种不同的情况:
global_var = 'global'
def foo():
unused_cvar = 'unused' # actually not a closure variable at all
readonly_cvar = 'closure'
nonlocal_cvar = 'nonlocal'
def bar(param):
nonlocal nonlocal_cvar
local_var = 'local'
_ = readonly_cvar
nonlocal_cvar = 'nonlocal'
frame = inspect.currentframe()
assert resolve_variable('local_var', frame) == local_var
assert resolve_variable('param', frame) == param
assert resolve_variable('unused_cvar', frame) == 'unused'
assert resolve_variable('readonly_cvar', frame) == readonly_cvar
assert resolve_variable('nonlocal_cvar', frame) == nonlocal_cvar
assert resolve_variable('global_var', frame) == global_var
bar('parameter')
foo()
如何重写我的 resolve_variable
函数以支持所有这些?有可能吗?
一般不可能。 Python 只保留闭包实际引用的闭包变量。
>>> import inspect
>>> class Demo(object):
... def __del__(self):
... print("Too late, it's gone.")
...
>>> def f():
... a = Demo()
... def g():
... return inspect.currentframe()
... return g
...
>>> frame = f()()
Too late, it's gone.
从这个例子可以看出,从框架 frame
中检查 a
是不可能的。没了。
至于框架实际使用的闭包变量,那些通常出现在f_locals
中。我知道一个奇怪的情况,他们不会,如果框架是针对带有闭包变量的 class 语句:
>>> def f():
... a = 1
... class Foo(object):
... print(a)
... print(inspect.currentframe().f_locals)
... return Foo
...
>>> f()
1
{'__module__': '__main__', '__qualname__': 'f.<locals>.Foo'}
<class '__main__.f.<locals>.Foo'>
在深入了解 CPython 实现后(特别是框架对象、LOAD_CLASSDEREF
操作码和 inspect.getclosurevars
),我认为访问 class 框架的唯一方法闭包变量将使用 ctypes
、gc.get_referents
或类似的令人讨厌的方法。
此外,请注意,如果闭包变量值在访问后发生变化,则 f_locals
字典可能不是最新的;再次访问 frame.f_locals
refreshes the contents,但在您查看时它可能已经过时了。