是否有办法在应用于同一函数的两个python装饰器之间传递变量?目标是让其中一个装饰者知道另一个也被应用了。我需要类似下面例子中的decobar_present():
def decobar(f):
def wrap():
return f() + "bar"
return wrap
def decofu(f):
def wrap():
print decobar_present() # Tells me whether decobar was also applied
return f() + "fu"
return wrap
@decofu
@decobar
def important_task():
return "abc"
更一般地说,我希望能够根据是否也应用了decobar来修改decofu的行为。
答案 0 :(得分:1)
您可以在应用decobar
时将该功能添加到“注册表”,然后检查注册表以确定是否已将decobar
应用于该功能。这种方法需要保留原始函数的__module__
和__name__
属性(在包装函数上使用functools.wraps
)。
import functools
class decobar(object):
registry = set()
@classmethod
def _func_key(cls, f):
return '.'.join((f.__module__, f.func_name))
@classmethod
def present(cls, f):
return cls._func_key(f) in cls.registry
def __call__(self, f):
self.registry.add(self._func_key(f))
@functools.wraps(f)
def wrap():
return f() + "bar"
return wrap
# Make the decorator singleton
decobar = decobar()
def decofu(f):
@functools.wraps(f)
def wrap():
print decobar.present(f) # Tells me whether decobar was also applied
return f() + "fu"
return wrap
@decofu
@decobar
def important_task():
return "abc"
使用一个类来实现decobar
,因为它将registry
和present()
保留在一个命名空间中(感觉更清晰,IMO)
答案 1 :(得分:0)
虽然可以做一些事情,比如操纵stack trace,但我认为,你最好创建一个函数decofubar
,并尽可能多地合并“fu”和“bar”尽可能。至少,它会使您的代码更清晰,更明显。
答案 2 :(得分:0)
每个装饰器都会包装另一个函数。传递给decofu()
的函数是decobar()
装饰器的结果。
只需测试decobar
包装器的特定特征,只要您能够识别包装器:
def decobar(f):
def wrap():
return f() + "bar"
wrap.decobar = True
return wrap
def decofu(f):
def wrap():
print 'decobar!' if getattr(f, 'decobar') else 'not decobar'
return f() + "fu"
return wrap
我在包装函数上使用了一个任意属性,但你可以尝试测试一个名称(不是那么明确),签名(或许使用inspect.getargspec()
)等等。
仅限于直接包装。
一般来说,你不想像所有这些一样紧密地装饰装饰器。制定出不同的解决方案,仅依赖于函数签名或返回值。
答案 3 :(得分:0)
您可以在f
中为wrap
(或更确切地说decobar
)分配旗帜,就像这样
def decobar(f):
def wrap():
return f() + "bar"
wrap.decobar_applied = True
return wrap
def decofu(f):
def wrap():
if hasattr(f, 'decobar_applied') and f.decobar_applied:
print decobar_present() # Tells me whether decobar was also applied
return f() + "fu"
return wrap
@decofu
@decobar
def important_task():
return "abc"
答案 4 :(得分:0)
要在两个python装饰器之间传递变量,可以使用装饰函数的关键字参数字典。在第二个装饰器中调用函数之前,不要忘记从那里弹出添加的参数。
def decorator1(func):
def wrap(*args, **kwargs):
kwargs['cat_says'] = 'meow'
return func(*args, **kwargs)
return wrap
def decorator2(func):
def wrap(*args, **kwargs):
print(kwargs.pop('cat_says'))
return func(*args, **kwargs)
return wrap
class C:
@decorator1
@decorator2
def spam(self, a, b, c, d=0):
print("Hello, cat! What's your favourite number?")
return a + b + c + d
x=C()
print(x.spam(1, 2, 3, d=7))