关于函数变量范围和返回,我有一个相当奇怪的问题。被调用函数返回值后,有什么方法可以检查调用者中被调用函数的范围?
用例很简单:在 Flask 视图中,我想绕过locals()
我的模板。我可以按照惯例定义我需要的模板,并且在每个视图中都返回一个字典让我很困扰。
关于函数变量范围和返回,我有一个相当奇怪的问题。被调用函数返回值后,有什么方法可以检查调用者中被调用函数的范围?
用例很简单:在 Flask 视图中,我想绕过locals()
我的模板。我可以按照惯例定义我需要的模板,并且在每个视图中都返回一个字典让我很困扰。
函数返回后,其作用域不再存在。这使得在函数返回后获取范围变得有问题。
但是,使用 Python 的跟踪或配置文件功能,可以在函数即将返回时运行一些代码,并在那时从其堆栈帧中提取局部变量。然后可以将它们隐藏在某个地方,并使用包装函数与(或代替)被调用函数的返回值一起返回。
这是一个可以用于这个邪恶目的的装饰器。请记住,这个实现是一个可怕的 hack,仅仅为了方便而使用它是不好的风格。我可能会想到合法用途……给我几天时间。此外,它可能不适用于非 CPython 实现(实际上可能不会)。
import sys, functools
def givelocals(func):
localsdict = {}
def profilefunc(frame, event, arg):
if event == "call":
localsdict.clear()
elif event == "return":
localsdict.update(frame.f_locals)
return profilefunc
@functools.wraps(func)
def wrapper(*args, **kwargs):
oldprofilefunc = sys.getprofile()
sys.setprofile(profilefunc)
try:
return func(*args, **kwargs), dict(localsdict)
except Exception as e:
e.locals = dict(localsdict)
raise
finally:
sys.setprofile(oldprofilefunc)
return wrapper
例子:
@givelocals
def foo(x, y):
a = x + y
return x * y
>>> foo(3, 4)
(12, {'y': 4, 'x': 3, 'a': 7})
如果你有一些你想使用的任意函数,因为它在你没有编写的模块中,所以你不能装饰它,你可以动态创建包装器并调用它:
def foo(x, y):
a = x + y
return x * y
>>> givelocals(foo)(3, 4)
(12, {'y': 4, 'x': 3, 'a': 7})
或者存储包装器并稍后调用它:
locals_foo = givelocals(foo)
>>> locals_foo(3, 4)
(12, {'y': 4, 'x': 3, 'a': 7})
包装器返回实际返回值和本地字典的元组。如果引发异常,.locals
则异常对象的属性设置为 locals dict。
请记住,通常 Python 在函数返回时会释放局部变量使用的内存。保留这些值将导致您的程序使用更多内存(只要有对它们的引用),因此当您不再需要它们时清理它们是个好主意。
最后一点:我在这里使用 Python 的配置文件功能,因为它仅在函数调用和返回时调用。如果您使用的是 2.6 之前的 Python 版本,则没有分析功能,因此您需要使用跟踪。profile 函数也可以用作编写的跟踪函数,您只需要使用gettrace()
andsettrace()
而不是相应的 profile 相关函数。但是,由于对每一行都调用了跟踪,因此包装的函数可能会明显变慢。