0

关于函数变量范围和返回,我有一个相当奇怪的问题。被调用函数返回值后,有什么方法可以检查调用者中被调用函数的范围?

用例很简单:在 Flask 视图中,我想绕过locals()我的模板。我可以按照惯例定义我需要的模板,并且在每个视图中都返回一个字典让我很困扰。

4

1 回答 1

2

函数返回后,其作用域不再存在。这使得在函数返回后获取范围变得有问题。

但是,使用 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 相关函数。但是,由于对每一行都调用了跟踪,因此包装的函数可能会明显变慢。

于 2013-01-02T15:46:21.227 回答