裝飾器(無參)python
它是一個函數;app
函數做爲它的形參;ide
返回值也是一個函數;函數
能夠使用@functionname方式,簡化調用;spa
裝飾器和高階函數orm
裝飾器是高階函數,但裝飾器是對傳入函數的功能的裝飾(功能加強)對象
import datetime import time def logger(fn): def wrap(*args, **kwargs): #before 功能加強 print("args={},kwargs={}".format(args, kwargs)) start = datetime.datetime.now() ret = fn(*args, **kwargs) #after 功能加強 duration = datetime.datetime.now() - start print("function {} took {}s.".format(fn.__name__, duration.total_seconds())) return ret return wrap @logger def add(x, y): print("======call add======") time.sleep(2) return x + y print(add(4, y=5))
講一個新的小知識點---文檔字符串
文檔
python的文檔字符串
python是文檔字符串Documentation Stringsit
在函數語句塊的第一行,且習慣是多行的文本,因此多使用三引號;
慣例是首字母大寫,第一行寫概述,空一行,第三行寫詳細描述;
能夠使用特殊屬性__doc__訪問這個文檔
def add(x, y): """This is a function of addition""" a = x + y return x + y print("name={}\ndoc={}".format(add.__name__, add.__doc__)) print(help(add))
這就是文檔字符串,經過文檔字符串能夠查看這個函數的幫助等一些信息
咱們在來看一段代碼,它的輸出結果是什麼呢?
import datetime import time def logger(fn): def wrap(*args, **kwargs): """This is a wrapper""" #before 功能加強 print("args={},kwargs={}".format(args, kwargs)) start = datetime.datetime.now() ret = fn(*args, **kwargs) #after 功能加強 duration = datetime.datetime.now() - start print("function {} took {}s.".format(fn.__name__, duration.total_seconds())) return ret return wrap @logger def add(x, y): """This is a function""" print("======call add======") time.sleep(2) return x + y # print(add(4, y=5)) print(add.__name__, add.__doc__,sep='\n')
運行結果以下:
wrap
This is a wrapper
經過代碼也能看出來,使用裝飾器是有反作用的:
原函數對象的屬性都被替換了,而使用裝飾器,咱們的需求是查看被封裝函數的屬性,如何解決?
想一下,這個函數的調用爲何要寫到79行???插入到其它行,行不行?這個本身考慮想一想吧,這裏就不提了。
既然咱們學會了裝飾器,那如何把copy_properties改形成裝飾器?這就引出了咱們的帶參裝飾器
import datetime import time def copy_properties(src): def wrapper(dst): dst.__name__ = src.__name__ dst.__doc__ = src.__doc__ dst.__qualname__ = src.__qualname__ return dst return wrapper def logger(fn): @copy_properties(fn)# copy_properties.wrapper(logger.wrap), def wrap(*args, **kwargs): """This is a wrapper""" #before 功能加強 print("args={},kwargs={}".format(args, kwargs)) start = datetime.datetime.now() ret = fn(*args, **kwargs) #after 功能加強 duration = datetime.datetime.now() - start print("function {} took {}s.".format(fn.__name__, duration.total_seconds())) return ret return wrap @logger def add(x, y): """This is a function""" print("======call add======") time.sleep(2) return x + y # print(add(4, y=5)) print(add.__name__, add.__doc__, add.__qualname__, sep='\n')
經過copy_properties函數將包裝函數的屬性覆蓋掉包包裝函數;
凡是被裝飾的函數都須要複製這些屬性,這個函數很通用;
能夠將複製屬性的函數構建成裝飾器函數,帶參裝飾器;
需求:獲取函數的執行時長,對時長超過閾值的函數記錄一下:
import datetime import time def logger(t):# def logger(t1, t2, t3....tn): def _logger(fn): #@copy_properties(fn) 能夠把上面寫的複製屬性的函數裝飾在此 def wrap(*args, **kwargs): #before 功能加強 # print("args={},kwargs={}".format(args, kwargs)) start = datetime.datetime.now() ret = fn(*args, **kwargs) #after 功能加強 duration = (datetime.datetime.now() - start).total_seconds() if duration > t: print("function {} took {}s.".format(fn.__name__, duration)) return ret return wrap return _logger @logger(3)# add = logger(3)(add), @logger(3, 5, 9,...n) def add(x, y): print("======call add======") time.sleep(5) return x + y print(add(4, y=5))
裝飾器(帶參)
它是一個函數;
函數做爲它的形參;
返回值是一個不帶參的裝飾器函數;
使用@functionname(參數列表)方式調用;
能夠看作在裝飾器外層又加了一層函數;
將記錄的功能提取出來,這樣就能夠經過外部提供的函數來靈活的控制輸出:
def logger(duration, func=lambda name, duration: print('{} took {}s'.format(name, duration))): def _logger(fn): @copy_properties(fn): def wrapper(*args, **kwargs): start = datetime.datetime.now() ret = fn(*args, **kwargs) delta = (datetime.datetime.now() - start).total_seconds() if delta > duration: func(fn.__name__, duration) return ret return wrapper return _logger