2013-07-19 32 views
0

是否有一种方法可以在应用于相同函数的两个python装饰器之间传递变量?目标是让其中一个装饰者知道另一个也被应用。我需要从下面的例子类似decobar_present():在两个python装饰器之间传递变量

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

为什么需要这个?这些装饰器在做什么? – user2357112

回答

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,因为它使registrypresent()在单个命名空间(这感觉性能稍微清洁,IMO)

+0

我想我会为所有装饰者实现一个中央注册表。感谢上面的想法! –

0

虽然有可能做类似操纵stack trace的事情,但我认为最好是创建一个函数decofubar,并尽可能多地包含“fu”和“bar”。至少,它会让你的代码更清晰,更明显。

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

这仅限于直接包装只。

一般来说,你不想像所有这些一样紧密地结合装饰者。制定一个不同的解决方案,只依赖函数签名或返回值。

+0

注意:这只适用于'decofu'和'decobar'之间没有其他装饰器的情况。 – user2357112

+0

@ user2357112:的确如此。 –

0

可以在decobar分配标志f(或相当wrap)就是这样的

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"