如何使用装饰器将变量注入范围?

发布时间:2023-03-07 / 作者:清心寡欲
本文介绍了如何使用装饰器将变量注入范围?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

c = '消息'定义装饰器工厂(值):def msg_decorator(f):definner_dec(*args, **kwargs):变量 = 值res = f(*args, **kwargs)返回资源返回内部_dec返回 msg_decorator@decorator_factory(c)def msg_printer():打印变量msg_printer()

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

NameError: global name 'var' is not defined

回溯甚至指向 var 的定义位置:

在inner_dec(*args, **kwargs)8 definner_dec(*args, **kwargs):9 变量 = 值--->10 res = f(*args, **kwargs)11 返回资源12 返回inner_dec

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

有没有办法做这样的事情?

解决方案

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

您希望实现的最佳目标是添加全局名称,使用函数的自己全局命名空间:

def decorator_factory(value):def msg_decorator(f):definner_dec(*args, **kwargs):g = f.__globals__ # 将 f.func_globals 用于 py <2.6哨兵=对象()oldvalue = g.get('var', sentinel)g['var'] = 值尝试:res = f(*args, **kwargs)最后:如果 oldvalue 是哨兵:del g['var']别的:g['var'] = 旧值返回资源返回内部_dec返回 msg_decorator

f.__globals__ 是包装函数的全局命名空间,因此即使装饰器位于不同的模块中,它也能工作.如果 var 已经被定义为一个全局变量,它会被替换为新的值,并且在调用该函数之后,全局变量被恢复.

这是有效的,因为函数中未分配给且未在周围作用域中找到的任何名称都被标记为全局.

演示:

>>>c = '消息'>>>@decorator_factory(c)... def msg_printer():... 打印变量...>>>msg_printer()信息>>>globals() 中的var"错误的

但是除了装饰,我也可以在全局范围内直接定义var.

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

[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]

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

The traceback even points to wher var is defined:

 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

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

Is there any way to do something like this?

解决方案

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

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__ 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.

Demo:

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

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.

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



[英文标题]How to inject variable into scope with a decorator?


声明:本媒体部分图片、文章来源于网络,版权归原作者所有,如有侵权,请联系QQ:330946442删除。