chapter5.2裝飾器
裝飾器
有一個需求,將一個加法函數,增加一個功能,輸出調用的參數,及調用信息
在源代碼裏插入代碼,叫硬編碼,不利於更改。非業務功能輸出信息,不應該放在業務代碼裏。
def add(x,y): """ function add """ return x+y def logger(fn,*args,**kwargs): print(‘sdfasd‘) ret = fn(*args,**kwargs) return ret print(logger(add,4,5))
定義兩個函數,調用後加強輸出,但是函數傳參是個問題,使用以上方法,*args和**kwargs
將函數柯裏化
def logger(fn): def wn(*args,**kwargs): print(‘Function is {}‘.format(wn.__name__)) wee = fn(*args,**kwargs) return wee return wn def add(x,y): """ function add """ return x+yprint(logger(add,4,5))
裝飾器語法糖
def logger(fn): def wn(*args,**kwargs):print(‘Function is {}‘.format(fn.__name__)) wee = fn(*args,**kwargs) return wee return wn @logger ##==>add = logger(add) def add(x,y): """ function add """ return x+y ret = add(4,4) print(ret)
裝飾器(無參)
是一個函數,函數作為形參,返回值也是函數,可以使用@Functionname方式調用
以上總結不準確,只是方便理解
裝飾器就是高階函數,但裝飾器是對傳入函數的功能的增強(裝飾)
add = logger(add)
這句中函數被重新覆蓋,但是原來的add指向的地址被logger中的fn引用,仍然存在。這裏fn使用了閉包
def logger(fn): def wrap(*args,**kwargs): ‘‘‘This is a warp‘‘‘ # print(‘function is {}‘.format(fn.__name__)) print(‘doc: {}‘.format(fn.__doc__)) ret = fn(*args,**kwargs) # return ret return wrap @logger def add(x,y): ‘‘‘This is a function of addition‘‘‘ return x+y ret = add(4,5) print(ret,add.__doc__)
這裏可以調用fn.__doc__查看文檔屬性,想要看原來add的文件屬性,在全局只能看到logger的,python中提供有相應的函數,要是自己實現,可以使用兩層裝飾器。外層可以使用帶參函數
帶參裝飾器
def copy(src): def copy_inner (dst): dst.__name__ = src.__name__ dst.__doc__ = src.__doc__ return dst return copy_inner def logger(fn): @copy(fn)####wrap = copy(fn)(wrap) def wrap(*args,**kwargs): ‘‘‘This is a warp‘‘‘ # print(‘function is {}‘.format(fn.__name__)) print(‘doc: {}‘.format(fn.__doc__)) ret = fn(*args,**kwargs) # return ret return wrap @logger###add = logger(add) def add(x,y): ‘‘‘This is a function of addition‘‘‘ return x+y ret = add(4,5) print(ret,add.__doc__)
以上函數,copy函數修飾了logger,將函數add的屬性復制到了函數warp上,只復制了名字和文檔的屬性
要想全部覆蓋,可以使用wrapper函數
from functools import wraps functools類下的wraps方法
functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=(‘__dict__‘,))
類似於copy_properties功能
wrapper包裝函數,被更新者,wrapper被包裝函數,數據源
元組WRAPPER__ASSIGNMENTS中是要覆蓋的屬性
(‘__module__‘, ‘__name__‘, ‘__qualname__‘, ‘__doc__‘, ‘__annotations__‘)
模塊名,名稱,限定名,文檔,參數註解
元組WRAPPER_UPDATES中是要被更新的屬性,__dict__屬性字典
增加一個__wrapper__屬性,保留wrapped的屬性
import functools def logger(fn): def wrap(*args,**kwargs): ‘‘‘This is a warp‘‘‘ print(‘function is {}‘.format(fn.__name__)) print(‘doc: {}‘.format(fn.__doc__)) ret = fn(*args,**kwargs) return ret return functools.update_wrapper(wrap,fn) @logger###add = logger(add) def add(x,y): ‘‘‘This is a function of addition‘‘‘ return x+y ret = add(4,5) print(ret,add.__doc__)
@functools.wraps( wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)
wrapped 被包裝函數
import functools def logger(fn): @functools.wraps(fn)###==>wrap = wraps(fn)(wrap) def wrap(*args,**kwargs): ‘‘‘This is a warp‘‘‘ print(‘function is {}‘.format(fn.__name__)) print(‘doc: {}‘.format(fn.__doc__)) ret = fn(*args,**kwargs) return ret return wrap @logger###add = logger(add) def add(x,y): ‘‘‘This is a function of addition‘‘‘ return x+y ret = add(4,5) print(ret,add.__doc__)
chapter5.2裝飾器