python 裝飾器
需求:
一個加法函數,想增強它的功能,能夠輸出加法函數的日誌信息
def add(x, y): return x + y
增加信息輸出功能:
def add(x, y): print("call add,x + y") #日誌輸出到控制台 return x + y
上面的加法函數是完成了需求,但是有以下的缺點:
打印日誌信息是一個功能,這條語句和 add 函數耦合太高;
加法函數屬於業務功能,而輸出日誌信息的功能,屬於非業務功能代碼,不該放在業務函數 add 中;
下面代碼做到了業務功能分離,但是 fn 函數調用傳參是個問題:
def add(x, y): return x + y def logger(fn): print('before') print('add function:{} {}'.format(4, 5)) ret = fn(4, 5) print('after') return ret print(logger(add))
解決了傳參的問題,進一步改變:
def add(x, y): return x + y def logger(fn, *args, **kwargs): print('before') print('add function:{} | {}'.format(args, kwargs)) ret = fn(*args, **kwargs) # 參數解構 print('after') return ret print(logger(add, 4, 5)) print(logger(add, x=4, y=6)) print(logger(add, 4, y=15))
柯里化:
def add(x, y): return x + y def logger(fn): def wrapper(*args, **kwargs): print('before') print('add function:{} | {}'.format(args, kwargs)) ret = fn(*args, **kwargs) # 參數解構 print('after') return ret return wrapper print(logger(add)(4, 5)) # 換一種寫法: ''' add = logger(add) print(add(x=5, y=10)) ''' # 1.先算等式右邊,logger(add) # 2.add函數對象被fn記住 # 3.logger函數返回 wrapper,即add = wrapper # 4.調用add(4, 5)即wrapper(4, 5) # 5.ret = fn(*args, **kwargs),此處的fn記住的是最原始的add函數對象
裝飾器語法糖:
def logger(fn): def wrapper(*args, **kwargs): print('before') print('add function:{} | {}'.format(args, kwargs)) ret = fn(*args, **kwargs) # 參數解構 print('after') return ret return wrapper @logger # 等價於add = logger(add) def add(x, y): return x + y print(add(20, 30)) ''' 可以採用逆推思維: 1.add(20, 30) 2.包裝add(20, 30)為:wrapper(20, 30) 3.實現日誌增強功能:logger(add)(20, 30) 即:add(20, 30) => wrapper(20, 30) => logger(add)(20, 30) def logger(fn): def wrapper(*args, **kwargs): val = fn(*args, **kwargs) return val return wrapper @logger def add(x, y): return x + y '''
@logger是什麼?這就是裝飾器語法
裝飾器和高階函數
裝飾器可以是高階函數,但裝飾器是對傳入函數的功能的裝飾(功能增強)