如何使用装饰器将变量注入作用域?
[免责声明:可能会有更多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
。
有什么办法可以做这样的事情吗?
-
你不能 作用域名称(闭包)在编译时确定,不能在运行时添加更多。
您可能希望实现的最好方法是使用函数 自己的 全局名称空间添加 全局 名称: __
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
在全局范围内进行定义。 __请注意,更改全局变量不是线程安全的,并且对同一模块中其他函数的任何瞬时调用也仍将看到该相同的全局变量。