2017-02-26 84 views
2

來自此post。接受的答案很好地與裝飾者沒有任何爭論。我試圖擴展這個解決方案,使它爲應用裝飾器帶來參數。編寫一個裝飾器,以便爲類的所有方法應用另一個裝飾器參數

詳細地說,我有使外部API調用的功能。由於這些調用經常失敗,我將此library的重試裝飾器應用於所有功能。爲了避免所有函數一次又一次地使用@retry(...)行,我決定將它們集中到一個類中。我創建了RetryClass,並將所有功能都作爲classmethod放入課程中。現在,我正在尋找一種方法來爲類的所有方法應用retry裝飾器,這樣我就可以不斷在類中添加新方法,並且會自動將新修飾器應用於新方法。

注意:重試裝飾器需要參數。

@retry(wait_random_min=100, wait_random_max=300, stop_max_attempt_number=3) 

這裏是我的代碼:

from retrying import retry 


def for_all_methods(decorator): 
    def decorate(cls): 
     for attr in cls.__dict__: 
      if callable(getattr(cls, attr)): 
       setattr(cls, attr, decorator(getattr(cls, attr))) 
     return cls 
    return decorate 


@for_all_methods(retry(wait_random_min=100, wait_random_max=300, stop_max_attempt_number=3)) 
class RetryClass(object): 

    @classmethod 
    def a(cls): 
     pass 


def test(): 
    RetryClass.a() 
    return 

這引發以下錯誤:

Traceback (most recent call last): 
    File "/Applications/PyCharm.app/Contents/helpers/pydev/pydevd.py", line 1596, in <module> 
    globals = debugger.run(setup['file'], None, None, is_module) 
    File "/Applications/PyCharm.app/Contents/helpers/pydev/pydevd.py", line 974, in run 
    pydev_imports.execfile(file, globals, locals) # execute the script 
    File "/Users/gyoho/Datatron/Dev/class-decorator/main.py", line 26, in <module> 
    test() 
    File "/Users/gyoho/Datatron/Dev/class-decorator/main.py", line 22, in test 
    RetryClass.a() 
TypeError: unbound method a() must be called with RetryClass instance as first argument (got nothing instead) 

然而,註釋掉類的裝飾,沒有錯誤運行。有什麼我失蹤?

+0

當你想將它應用到所有的方法,你可以用[元類更好](https://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python#6581949)。 – mdh

回答

0

classmethodstaticmethod必須是一個方法的最後裝飾,因爲它們返回descriptors,而不是一個功能。 (這是更難裝飾)。你可以發現,如果一個方法已經是classmethodstaticmethod,然後你decorate函數看起來有點像這樣:

def decorate(cls): 
    for attr in cls.__dict__: 
     possible_method = getattr(cls, attr) 
     if not callable(possible_method): 
      continue 

     if not hasattr(possible_method, "__self__"): 
      raw_function = cls.__dict__[attr].__func__ 
      decorated_method = decorator(raw_function) 
      decorated_method = staticmethod(decorated_method) 

     if type(possible_method.__self__) == type: 
      raw_function = cls.__dict__[attr].__func__ 
      decorated_method = decorator(raw_function) 
      decorated_method = classmethod(decorated_method) 

     elif possible_method.__self__ is None: 
      decorated_method = decorator(possible_method) 

     setattr(cls, attr, decorated_method) 

    return cls 
+0

也許,僅僅從類中實例化一個對象並使用它的方法會更容易一些呢? – gyoho

+0

@gyoho它可能會更簡單,bur記住你仍然有'staticmethod'和'property'的問題 –

+0

我刪除了'@ classmethod'並將'cls'改爲'self',但沒有把'@ staticmethod'。這次運行良好。這樣做有什麼缺點? – gyoho

1

問題是@classmethod不再是a()的頂級裝飾器。 RetryClass.a目前按照@classmethod@retry的順序進行裝飾。 RetryClass等同於:

class RetryClass(object): 

    @retry(wait_random_min=100, wait_random_max=300, stop_max_attempt_number=3) 
    @classmethod 
    def a(cls): 
     pass 

類別需要等同於:

class RetryClass(object): 

    @classmethod 
    @retry(wait_random_min=100, wait_random_max=300, stop_max_attempt_number=3) 
    def a(cls): 
     pass 
+0

我想我只需要避免'classmethod'或'staticmethod'。實例化這個類並使用它的方法? – gyoho