是否有可能在Python中看到被调用函数的变量范围?

时间:2013-01-02 15:39:16

标签: python reflection scoping

我对函数变量范围和返回有一个相当奇怪的问题。 有没有办法在被调用函数返回值后检查调用者中被调用函数的范围?

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

1 个答案:

答案 0 :(得分:1)

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

但是,使用Python的跟踪或配置文件功能,可以运行一些代码,就像函数 about 一样,并在那时从其堆栈帧中提取本地。然后可以将它们放在某处,并使用包装函数返回(或代替)被调用函数的返回值。

这是一个可以用于这个邪恶目的的装饰器。请记住,这个实现是一个可怕的黑客,使用它只是为了方便是不好的风格。我可能会想到合法用途......给我几天时间。此外,它可能无法与非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属性将设置为locals dict。

最后一点:我在这里使用Python的配置文件功能,因为它仅在函数调用和返回时调用。如果您使用的是2.6之前的Python版本,那么您没有分析,因此您需要使用跟踪代替。配置文件功能也可以作为书面跟踪功能使用,您只需使用gettrace()settrace()而不是相应的配置文件相关功能。但是,由于为每一行调用了跟踪,因此包装函数可能会明显变慢。