如何使用装饰器将变量注入作用域?

发布于 2021-01-29 18:16:41

[免责声明:可能会有更多pythonic方法来做我想做的事,但我想知道python的作用域在这里是如何工作的]

我试图找到一种方法来制作装饰器,该装饰器的作用类似于将名称注入另一个函数的作用域(以使名称不会泄漏到装饰器作用域之外)。例如,如果我有一个函数说要打印一个var尚未定义的名为的变量,我想在调用它的装饰器中定义它。这是一个中断的示例:

c = 'Message'

def decorator_factory(value):
    def msg_decorator(f):
        def inner_dec(*args, **kwargs):
            var = value
            res = f(*args, **kwargs)
            return res
        return inner_dec
    return msg_decorator

@decorator_factory(c)
def msg_printer():
    print var

msg_printer()

我希望它打印“ Message”,但它给出:

NameError: global name 'var' is not defined

追溯甚至指向whlevar定义:

<ipython-input-25-34b84bee70dc> in inner_dec(*args, **kwargs)
      8         def inner_dec(*args, **kwargs):
      9             var = value
---> 10             res = f(*args, **kwargs)
     11             return res
     12         return inner_dec

所以我不明白为什么找不到它var

有什么办法可以做这样的事情吗?

关注者
0
被浏览
46
1 个回答
  • 面试哥
    面试哥 2021-01-29
    为面试而生,有面试问题,就找面试哥。

    你不能 作用域名称(闭包)在编译时确定,不能在运行时添加更多。

    您可能希望实现的最好方法是使用函数 自己的 全局名称空间添加 全局 名称: __

    def decorator_factory(value):
        def msg_decorator(f):
            def inner_dec(*args, **kwargs):
                g = f.__globals__  # use f.func_globals for py < 2.6
                sentinel = object()
    
                oldvalue = g.get('var', sentinel)
                g['var'] = value
    
                try:
                    res = f(*args, **kwargs)
                finally:
                    if oldvalue is sentinel:
                        del g['var']
                    else:
                        g['var'] = oldvalue
    
                return res
            return inner_dec
        return msg_decorator
    

    f.__globals__是包装函数的全局名称空间,因此即使装饰器位于其他模块中也可以使用。如果var已经定义为全局变量,则将其替换为新值,并在调用函数后恢复全局变量。

    之所以可行,是因为在函数中任何未分配给且在周围范围中找不到的名称都被标记为全局名称。

    演示:

    >>> c = 'Message'
    >>> @decorator_factory(c)
    ... def msg_printer():
    ...     print var
    ... 
    >>> msg_printer()
    Message
    >>> 'var' in globals()
    False
    

    但是除了修饰,我也可以 直接var在全局范围内进行定义。 __

    请注意,更改全局变量不是线程安全的,并且对同一模块中其他函数的任何瞬时调用也仍将看到该相同的全局变量。



知识点
面圈网VIP题库

面圈网VIP题库全新上线,海量真题题库资源。 90大类考试,超10万份考试真题开放下载啦

去下载看看