2016-02-08 34 views
1

我有一個班的工作是包裝另一個類(我不控制的代碼),攔截所有對包裝類的調用,執行一些邏輯,並將調用傳遞給底層類。這裏有一個例子:定義或記錄由__getattr__處理的呼叫的正確方法是什麼?

class GithubRepository(object): 

    def get_commit(self, sha): 
     return 'Commit {}'.format(sha) 

    def get_contributors(self): 
     return ['bobbytables'] 


class LoggingGithubRepositoryWrapper(object): 

    def __init__(self, github_repository): 
     self._github_repository = github_repository 

    def __getattr__(self, name): 
     base_func = getattr(self._github_repository, name) 

     def log_wrap(*args, **kwargs): 
      print "Calling {}".format(name) 
      return base_func(*args, **kwargs) 

     return log_wrap 

if __name__ == '__main__': 

    git_client = LoggingGithubRepositoryWrapper(GithubRepository()) 

    print git_client.get_commit('abcdef1245') 
    print git_client.get_contributors() 

正如你所看到的,我這樣做是在包裹類實現__getattr__,並委託給底層階級的方式。這種方法的缺點是LoggingGithubRepositoryWrapper的用戶不知道底層GithubRepository實際上具有哪些屬性/方法。

這使我對我的問題:有沒有一種方法來定義或記錄由__getattr__處理的呼叫?理想情況下,我希望能夠自動完成git_client.並提供支持的方法列表。感謝您的幫助!

+0

不,沒有 - Python是高度動態的,所以一旦你把'__getattr__'放在混合中,或多或少不可能告訴什麼可以工作。 – jonrsharpe

回答

1

您可以通過幾種不同的方式來做到這一點,但他們不會涉及使用__getattr__

你真正需要做的是動態創建你的類,或者至少動態地在你的類上創建包裝函數。有幾種方法可以在Python中執行此操作。

您可以使用type()或元類來構建類定義,或者使用__new__方法在類實例化上構建類定義。

每當您撥打LoggingGithubRepositoryWrapper()時,都會調用__new__方法。在這裏,它查看github_repository參數的所有屬性,並查找所有非私有方法。然後它在實例化的LoggingGithubRepositoryWrapper類實例上創建一個函數,該實例在日誌語句中封裝了repo調用。

最後,它返回修改後的類實例。然後調用__init__

from types import MethodType 


class LoggingGithubRepositoryWrapper(object): 

    def __new__(cls, github_repository): 
     self = super(LoggingGithubRepositoryWrapper, cls).__new__(cls) 
     for name in dir(github_repository): 
      if name.startswith('__'): 
       continue 
      func = getattr(github_repository, name)    
      if isinstance(func, MethodType):     
       setattr(self, name, cls.log_wrap(func)) 
     return self 

    @staticmethod 
    def log_wrap(func): 
     def wrap(*args, **kwargs): 
      print 'Calling {0}'.format(func.__name__) 
      return func(*args, **kwargs) 
     return wrap 

    def __init__(self, github_repository): 
     ... # this is all the same 
相關問題