目前在中文網上能搜索到的絕大部分關于裝飾器的教程,都在講如何裝飾一個普通的函數。本文介紹如何使用Python的裝飾器裝飾一個類的方法,同時在裝飾器函數中調用類里面的其他方法。本文以捕獲一個方法的異常為例來進行說明。
有一個類Test, 它的結構如下:
class Test(object):
def __init__(self):
pass
def revive(self):
print('revive from exception.')
# do something to restore
def read_value(self):
print('here I will do something.')
# do something.
在類中有一個方法read_value()
,這個方法在多個地方被調用。由于某些原因,方法read_value
有可能隨機拋出Exception導致程序崩潰。所以需要對整個方法做try ... except
處理。最丑陋的做法如下面的代碼所示:
class Test(object):
def __init__(self):
pass
def revive(self):
print('revive from exception.')
# do something to restore
def read_value(self):
try:
print('here I will do something.')
# do something.
except Exception as e:
print(f'exception {e} raised, parse exception.')
# do other thing.
self.revive()
這樣寫雖然可以解決問題,但是代碼不Pythonic。
使用裝飾器來解決這個問題,裝飾器函數應該寫在類里面還是類外面呢?答案是,寫在類外面。那么既然寫在類外面,如何調用這個類的其他方法呢?
首先寫出一個最常見的處理異常的裝飾器:
def catch_exception(origin_func):
def wrapper(*args, **kwargs):
try:
u = origin_func(*args, **kwargs)
return u
except Exception:
return 'an Exception raised.'
return wrapper
class Test(object):
def __init__(self):
pass
def revive(self):
print('revive from exception.')
# do something to restore
@catch_exception
def read_value(self):
print('here I will do something.')
# do something.
這種寫法,確實可以捕獲到origin_func()
的異常,但是如果在發生異常的時候,需要調用類里面的另一個方法來處理異常,這又應該怎么辦?答案是給wrapper增加一個參數:self.
代碼變為如下形式:
def catch_exception(origin_func):
def wrapper(self, *args, **kwargs):
try:
u = origin_func(self, *args, **kwargs)
return u
except Exception:
self.revive() #不用顧慮,直接調用原來的類的方法
return 'an Exception raised.'
return wrapper
class Test(object):
def __init__(self):
pass
def revive(self):
print('revive from exception.')
# do something to restore
@catch_exception
def read_value(self):
print('here I will do something.')
# do something.
只需要修改裝飾器定義的部分,使用裝飾器的地方完全不需要做修改。
下圖為正常運行時的運行結果:
正常運行
下圖為發生異常以后捕獲并處理異常:
發生異常
通過添加一個self參數,類外面的裝飾器就可以直接使用類里面的各種方法,也可以直接使用類的屬性。