裝飾器(Decorator)
裝飾器推導過程
- 需求
- 一個加法函數,想增強它的功能,能夠輸出被調用過以及調用的參數信息
def add(x,y): return x + y
- 增加信息輸出功能
def add(x,y): print('call {},{}+{}'.format(add.__name__, x, y)) return x + y
- 增加信息輸出功能
- 不足:
- 打印語句的耦合太高
- 加法函數屬于業務功能,而輸出信息的功能,屬于非業務功能代碼,不該放在業務函數加法中
- 業務功能分離
def add(x,y): return x+ydef logger(fn): print('begin') #增強的輸出 ret = fn(4,7) print('end') #增強的功能 return retlogger(add)
- 但是fn函數傳參是一個問題
- 但是fn函數傳參是一個問題
- 解決傳參
def add(x,y): return x+ydef logger(fn,*args, **kwargs): print('begin') ret = fn(*args, **kwargs) print('end') return retlogger(add,7,9)
- 但又想進一步的簡化
- 柯里化
def add(x,y): return x+y def logger(fn): def wrapper(*args, **kwargs): print('begin') ret = fn(*args, **kwargs) print('end') return ret return wrapper logger(add)(6,5)
- 調用可以換一種寫法
add = logger(add) add(6,5)
- 調用可以換一種寫法
- 裝飾器語法糖
def logger(fn): def wrapper(*args, **kwargs): print('begin') x = fn(*args, **kwargs) print('end') return x return wrapper```@logger #等價于 add = logger(add)```def add(x,y): return x+yadd(6,43)
- 一個加法函數,想增強它的功能,能夠輸出被調用過以及調用的參數信息
無參裝飾器
- 裝飾器(無參)
- 它是一個函數
- 函數作為它的形參
- 返回值也是一個函數
- 可以使用@functionname方式,簡化調用
- 裝飾器和高階函數
- 裝飾器是高階函數,傳入和輸出都是函數。
- 但裝飾器是對傳入函數的功能裝飾(功能增強)
應用
獲取函數的執行時長,對時長超過閾[yù]值的函數記錄一下
import datetime
import time
def logger(fn):
def wrapper(*args, **kwargs):
start = datetime.datetime.now()
print('begin')
ret = fn(*args, **kwargs)
print('end')
end = (datetime.datetime.now() - start).total_seconds()
print('function {} took {}s'.format(fn.__name__,end))
return ret
return wrapper
@logger
def add(x,y):
print('===call add=========')
time.sleep(3)
return x+y
文檔字符串
- python的文檔
- python是文檔字符串Documentation Strings
- 在函數語句塊的第一行,且習慣是多行的文本,所以多使用三引號
- 慣例是首字母大寫,第一行寫概述,空一行,第三行寫詳細描述
- 可以使用特殊屬性doc訪問這個文檔
def add(x,y):
"""
This is a function of addition
int x
int y
return int
"""
a = x+y
return x + y
print("name={}\ndoc={}".format(add.__name__, add.__doc__))
print(help(add))
文檔字符串應用
凡是被裝飾的函數都需要復制這些屬性,這個函數很通用
- 副作用
def logger(fn): def wrap(*args,**kwargs): ''' This is a wrapper ''' print('begin') ret = fn(*args,**kwargs) print('end') return ret return wrap @logger def add(x,y): ''' This is a function of addition int x int y return int ''' return x + y print('name = {}, doc = {}'.format(add.__name__,add.__doc__))
- 原函數對象的屬性都被替換了,而使用裝飾器,我們的需求是查看被封裝函數的屬性
- 也就是想看到add的屬性,結果顯示的是wrap的
- 如何解決?
- 提供一個函數,被封裝函數屬性 copy 成 包裝函數屬性
def copy_properties(src,dst): dst.__name__ = src.__name__ dst.__doc__ = src.__doc__ def logger(fn): def wrap(*args,**kwargs): ''' This is a wrapper ''' print('begin') ret = fn(*args,**kwargs) print('end') return ret copy_properties(fn,wrap) return wrap @logger def add(x,y): ''' This is a function of addition int x int y return int ''' return x + y print('name = {}, doc = {}'.format(add.__name__,add.__doc__))
- copy_properties可以改造成裝飾器
def copy_properties(src): def _copy(dst): dst.__name__ = src.__name__ dst.__doc__ = src.__doc__ return dst return _copydef logger(fn): @copy_properties(fn) def wrap(*args,**kwargs): ''' This is a wrapper ''' print('begin') ret = fn(*args,**kwargs) print('end') return ret return wrap@loggerdef add(x,y): ''' This is a function of addition int x int y return int ''' return x + yprint('name = {}, doc = {}'.format(add.__name__,add.__doc__))#第9行等價于 wrap = copy_properties(fn)(wrap)
帶參裝飾器
- 它是一個函數
- 函數作為它的形參
- 返回值是一個不帶參的裝飾器函數
- 使用@functionname(參數列表)方式,簡化調用
- 可以看做在裝飾器外層又加了一層函數
應用
獲取函數的執行時長,對時長超過閾[yù]值的函數記錄一下
import datetime
import time
def logger(t1,t2):
def _logger(fn):
def wrapper(*args, **kwargs):
start = datetime.datetime.now()
print('begin')
ret = fn(*args, **kwargs)
print('end')
delta = (datetime.datetime.now() - start).total_seconds()
if delta > t1 and delta < t2:
print('function {} took {}s'.format(fn.__name__,delta)
return ret
return wrapper
return _logger
@logger(3,5)
def add(x,y):
print('===call add=========')
time.sleep(6)
return x+y
- 將記錄的功能提取出來,這樣就可以通過外部提供的函數來靈活的控制輸出
import datetime import time def logger(duration, func = lambda name,t1: print('{} took {}s'.format(name,t1))): def _logger(fn): def wrapper(*args, **kwargs): start = datetime.datetime.now() print('begin') ret = fn(*args, **kwargs) print('end') delta = (datetime.datetime.now() - start).total_seconds() if delta > duration: func(fn.__name__, delta) return ret return wrapper return _logger @logger(3) def add(x,y): print('===call add=========') time.sleep(6) return x+y
本文來自投稿,不代表Linux運維部落立場,如若轉載,請注明出處:http://www.www58058.com/88024