如何使用装饰器将变量注入作用域? [英] How to inject variable into scope with a decorator?

查看:77
本文介绍了如何使用装饰器将变量注入作用域?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

[Disclaimer: there may be more pythonic ways of doing what I want to do, but I want to know how python's scoping works here]

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

I'm trying to find a way to make a decorator that does something like injecting a name into the scope of another function (such that the name does not leak outside the decorator's scope). For example, if I have a function that says to print a variable named var that has not been defined, I would like to define it within a decorator where it is called. Here is an example that breaks:

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()

我希望它打印 消息,但它给出:

I would like it to print "Message", but it gives:

NameError: global name 'var' is not defined

回溯甚至指向 var 定义为:

<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

So I don't understand why it can't find var.

有没有

推荐答案

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

You can't. Scoped names (closures) are determined at compile time, you cannot add more at runtime.

最好能实现的是添加 global 名称,使用函数的 own 全局命名空间:

The best you can hope to achieve is to add global names, using the function's own global namespace:

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 已经被定义为全局变量,则将其替换为新值,并在调用该函数后恢复全局变量。

f.__globals__ is the global namespace for the wrapped function, so this works even if the decorator lives in a different module. If var was defined as a global already, it is replaced with the new value, and after calling the function, the globals are restored.

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

This works because any name in a function that is not assigned to, and is not found in a surrounding scope, is marked as a global instead.

演示:

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

并在全局范围 中直接定义了 var

But instead of decorating, I could just as well have defined var in the global scope directly.

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

Note that altering the globals is not thread safe, and any transient calls to other functions in the same module will also still see this same global.

这篇关于如何使用装饰器将变量注入作用域?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆