2013-07-19 44 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"