裝飾器的作用與原理相當于java中的注解 我們可以在不入侵一個函數的情況下,在這個函數前和函數后,執(zhí)行一些預設的功能。通常我們會把函數中都會用到的某一類功能(如:鑒權、日志、參數校驗等)封裝成一個裝飾器,這樣代碼更簡潔,語義也更清晰。 在python中用@ 裝飾器函數,簡化了上述設計模式的使用過程 我們來看一個簡單應用 def log(fn): def wrap_function(): print('準備運行函數fn') fn() print('函數fn運行結束') return wrap_function@logdef fnA(): print('正在運行fnA....')fnA()# 輸出:'''準備運行函數fn正在運行fnA....函數fn運行結束''' 與下面的代碼是等價的
使用擴展
def log(fn): def wrap_function(a): print('原函數入參...', a) print('準備運行函數fn') fn(a) print('函數fn運行結束') return wrap_function @log def fnA(a): print('正在運行fnA...', a) fnA('入參A') # 輸出: ''' 原函數入參... 入參A 準備運行函數fn 正在運行fnA... 入參A 函數fn運行結束 '''
函數裝飾器嵌套執(zhí)行順序先由上到下執(zhí)行before,執(zhí)行完原函數后,再由下往上執(zhí)行After函數 def deA(fn): def wrap_function(*args, **kwargs): print('deA原函數入參...', args) print('deA準備運行函數fn') fn(*args, **kwargs) print('deA函數fn運行結束') return wrap_functiondef deB(fn): def wrap_function(*args, **kwargs): print('deB原函數入參...', args) print('deB準備運行函數fn') fn(*args, **kwargs) print('deB函數fn運行結束') return wrap_functiondef deC(fn): def wrap_function(*args, **kwargs): print('deC原函數入參...', args) print('deC準備運行函數fn') fn(*args, **kwargs) print('deC函數fn運行結束') return wrap_function@deA@deB@deCdef fnA(a, b): print('正在運行fnA...', a, b)fnA('入參A', '入參B')# 輸出:'''deA原函數入參... ('入參A', '入參B')deA準備運行函數fndeB原函數入參... ('入參A', '入參B')deB準備運行函數fndeC原函數入參... ('入參A', '入參B')deC準備運行函數fn正在運行fnA... 入參A 入參BdeC函數fn運行結束deB函數fn運行結束deA函數fn運行結束''' 帶參數的裝飾器在上述基本的裝飾器的基礎上,在外面套一層接收參數的函數,來實現裝飾器帶參數功能
Python常用的內置裝飾器@staticmethod、 @classmethod、 @property、@abstractmethod
class B: @staticmethod def add(a, b): return a + b B.add(10, 5) # 15
class B: def __init__(self): self._var1 = 3 self.var2 = 5 @property def var1(self): return self._var1b = B() b.var1 b.var1 = 10 # 嘗試給其賦值,會報錯
@wraps()語法糖由于Python的裝飾器是通過閉包實現的,所以在裝飾函數中,獲取導的屬性并非原函數,這就不是很完美 def logger(fn): def wrap_function(*args, **kwargs): ''' 這是裝飾器函數 ''' print('裝飾器中。。。。') return fn(*args, **kwargs) return wrap_function@loggerdef fnA(): ''' fnA中的打印 ''' print('正在運行fnA....')print(fnA.__name__, fnA.__doc__)# 輸出:'''wrap_function 這是裝飾器函數''' 加上@wraps() 后,修飾內部函數,保證這個內部函數帶有傳進來這個函數的屬性
類裝飾器通過 __call__ 將類變成調用對象 class Logger: def __init__(self, func): self.func = func def print_console(self): print('console print') def __call__(self, *args, **kwargs): self.print_console() self.func()@Loggerdef fnA(): print('fnA正在運行。。。')fnA()# 輸出'''console printfnA正在運行。。。''' 帶參數的類裝飾器
有了類裝飾器,就可以使用繼承了 from functools import wrapsclass Logger: def __init__(self, prefix='logger'): self.prefix = prefix def print_console(self): print('console print', self.prefix) def notify(self): self.print_console() def __call__(self, func): @wraps(func) def wrapper(*args, **kwargs): print(self.prefix, 'wrapper before...') self.notify() ret = func(*args, **kwargs) print(self.prefix, 'wrapper after...') return ret return wrapperclass EmailLogger(Logger): def __init__(self, prefix='Email', email='abc@test.com'): Logger.__init__(self, prefix) self.email = email def print_email(self): print('email print:', self.email) def notify(self): self.print_email()@EmailLogger(email='test@test.com')def fnA(): print('fnA正在運行。。。')fnA()# 輸出:'''Email wrapper before...email print: test@test.comfnA正在運行。。。Email wrapper after...''' |
|