Python入門篇-裝飾器
python
做者:尹正傑app
版權聲明:原創做品,謝絕轉載!不然將追究法律責任。ide
一.裝飾器概述函數
裝飾器(無參)
它是一個函數
函數做爲它的形參
返回值也是一個函數
能夠使用@functionname方式,簡化調用
裝飾器和高階函數
裝飾器是高階函數,但裝飾器是對傳入函數的功能的裝飾(功能加強)
帶參裝飾器
它是一個函數
函數做爲它的形參
返回值是一個不帶參的裝飾器函數
使用@functionname(參數列表)方式調用
能夠看作在裝飾器外層又加了一層函數
二.爲何要用裝飾器spa
1>.在不是用裝飾器的狀況下,給某個函數添加功能日誌
在解釋爲何使用裝飾器以前,完美來看一個需求:
一個加法函數,想加強它的功能,可以輸出被調用過以及調用的參數信息 def add(x, y): return x + y
增長信息輸出功能: def add(x, y): print("call add, x + y") # 日誌輸出到控制檯
return x + y
上面的加法函數是完成了需求,可是有如下的缺點 打印語句的耦合過高,換句話說,咱們不推薦去修改初始的add函數原始代碼。 加法函數屬於業務功能,而輸出信息的功能,屬於非業務功能代碼,不應放在業務函數加法中
2>.使用高階函數給某個函數添加功能code
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:y1053419035@qq.com 6 7 8 def add(x,y): 9 return x + y 10 11 def logger(func): 12 print('begin') # 加強的輸出 13 f = func(4,5) 14 print('end') # 加強的功能 15 return f 16 17 print(logger(add)) 18 19 20 21 #以上代碼輸出結果以下: 22 begin 23 end 24 9
3>.解決了傳參的問題,進一步改變orm
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:y1053419035@qq.com 6 7 8 def add(x,y): 9 return x + y 10 11 def logger(func,*args,**kwargs): 12 print('begin') # 加強的輸出 13 f = func(*args,**kwargs) 14 print('end') # 加強的功能 15 return f 16 17 print(logger(add,5,y=60)) 18 19 20 21 #以上代碼輸出結果以下: 22 begin 23 end 24 65
4>.柯里化實現add函數功能加強對象
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:y1053419035@qq.com 6 7 def add(x,y): 8 return x + y 9 10 def logger(fn): 11 def wrapper(*args,**kwargs): 12 print('begin') 13 x = fn(*args,**kwargs) 14 print('end') 15 return x 16 return wrapper 17 18 # print(logger(add)(5,y=50)) #海航代碼等價於下面兩行代碼,只是換了一種寫法而已 19 add = logger(add) 20 print(add(x=5, y=10)) 21 22 23 #以上代碼輸出結果以下: 24 begin 25 end 26 15
5>.裝飾器語法糖blog
#!/usr/bin/env python #_*_coding:utf-8_*_ #@author :yinzhengjie #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ #EMAIL:y1053419035@qq.com """ 定義一個裝飾器 """ 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 + y print(add(45,40)) #以上代碼輸出結果以下: begin end 85
三.幫助文檔之文檔字符串
1>.定義python的文檔字符串
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:y1053419035@qq.com 6 7 8 """ 9 Python的文檔 10 Python是文檔字符串Documentation Strings 11 在函數語句塊的第一行,且習慣是多行的文本,因此多使用三引號 12 慣例是首字母大寫,第一行寫概述,空一行,第三行寫詳細描述 13 能夠使用特殊屬性__doc__訪問這個文檔 14 """ 15 16 def add(x,y): 17 """This is a function of addition""" 18 a = x+y 19 return x + y 20 21 print("name = {}\ndoc = {}".format(add.__name__, add.__doc__)) 22 23 print(help(add)) 24 25 26 27 #以上代碼執行結果以下: 28 name = add 29 doc = This is a function of addition 30 Help on function add in module __main__: 31 32 add(x, y) 33 This is a function of addition 34 35 None
2>.裝飾器的反作用
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:y1053419035@qq.com 6 7 8 def logger(fn): 9 def wrapper(*args,**kwargs): 10 'I am wrapper' 11 print('begin') 12 x = fn(*args,**kwargs) 13 print('end') 14 return x 15 return wrapper 16 17 @logger #add = logger(add) 18 def add(x,y): 19 '''This is a function for add''' 20 return x + y 21 22 23 print("name = {}\ndoc= {}".format(add.__name__, add.__doc__)) #原函數對象的屬性都被替換了,而使用裝飾器,咱們的需求是查看被封裝函數的屬性,如何解決? 24 25 26 27 28 #以上代碼執行結果以下: 29 name = wrapper 30 doc= I am wrapper
3>.提供一個函數,被封裝函數屬性==copy==> 包裝函數屬性
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:y1053419035@qq.com 6 7 8 """ 9 經過copy_properties函數將被包裝函數的屬性覆蓋掉包裝函數 10 凡是被裝飾的函數都須要複製這些屬性,這個函數很通用 11 能夠將複製屬性的函數構建成裝飾器函數,帶參裝飾器 12 """ 13 def copy_properties(src, dst): # 能夠改形成裝飾器 14 dst.__name__ = src.__name__ 15 dst.__doc__ = src.__doc__ 16 17 def logger(fn): 18 def wrapper(*args,**kwargs): 19 'I am wrapper' 20 print('begin') 21 x = fn(*args,**kwargs) 22 print('end') 23 return x 24 copy_properties(fn, wrapper) 25 return wrapper 26 27 @logger #add = logger(add) 28 def add(x,y): 29 '''This is a function for add''' 30 return x + y 31 32 print("name = {}\ndoc = {}".format(add.__name__, add.__doc__)) 33 34 35 36 37 #以上代碼執行結果以下: 38 name = add 39 doc = This is a function for add
4>.提供一個函數,被封裝函數屬性==copy==> 包裝函數屬性,改形成帶參裝飾器
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:y1053419035@qq.com 6 7 8 def copy_properties(src): # 柯里化 9 def _copy(dst): 10 dst.__name__ = src.__name__ 11 dst.__doc__ = src.__doc__ 12 return dst 13 return _copy 14 15 def logger(fn): 16 @copy_properties(fn) # wrapper = copy_properties(fn)(wrapper) 17 def wrapper(*args,**kwargs): 18 'I am wrapper' 19 print('begin') 20 x = fn(*args,**kwargs) 21 print('end') 22 return x 23 return wrapper 24 25 @logger #add = logger(add) 26 def add(x,y): 27 '''This is a function for add''' 28 return x + y 29 30 print("name = {}\ndoc = {}".format(add.__name__, add.__doc__)) 31 32 33 34 #以上代碼執行結果以下: 35 name = add 36 doc = This is a function for add
四.裝飾器案例
1>.無參裝飾器
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:y1053419035@qq.com 6 7 import datetime 8 import time 9 10 """ 11 定義一個裝飾器 12 """ 13 def logger(fn): 14 def wrap(*args, **kwargs): 15 # before 功能加強 16 print("args={}, kwargs={}".format(args,kwargs)) 17 start = datetime.datetime.now() 18 ret = fn(*args, **kwargs) 19 # after 功能加強 20 duration = datetime.datetime.now() - start 21 print("function {} took {}s.".format(fn.__name__, duration.total_seconds())) 22 return ret 23 return wrap 24 25 @logger # 至關於add = logger(add),調用裝飾器 26 def add(x, y): 27 print("===call add===========") 28 time.sleep(2) 29 return x + y 30 31 print(add(4, y=7)) 32 33 34 35 #以上代碼輸出結果以下: 36 args=(4,), kwargs={'y': 7} 37 ===call add=========== 38 function add took 2.000114s. 39 11
2>.有參裝飾器
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:y1053419035@qq.com 6 7 import datetime,time 8 9 def copy_properties(src): # 柯里化 10 def _copy(dst): 11 dst.__name__ = src.__name__ 12 dst.__doc__ = src.__doc__ 13 return dst 14 return _copy 15 16 """ 17 定義裝飾器: 18 獲取函數的執行時長,對時長超過閾值的函數記錄一下 19 """ 20 def logger(duration): 21 def _logger(fn): 22 @copy_properties(fn) # wrapper = wrapper(fn)(wrapper) 23 def wrapper(*args,**kwargs): 24 start = datetime.datetime.now() 25 ret = fn(*args,**kwargs) 26 delta = (datetime.datetime.now() - start).total_seconds() 27 print('so slow') if delta > duration else print('so fast') 28 return ret 29 return wrapper 30 return _logger 31 32 @logger(5) # add = logger(5)(add) 33 def add(x,y): 34 time.sleep(3) 35 return x + y 36 37 print(add(5, 6)) 38 39 40 41 #以上代碼執行結果以下: 42 so fast 43 11
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:y1053419035@qq.com 6 7 import datetime,time 8 9 def copy_properties(src): # 柯里化 10 def _copy(dst): 11 dst.__name__ = src.__name__ 12 dst.__doc__ = src.__doc__ 13 return dst 14 return _copy 15 16 """ 17 定義裝飾器: 18 獲取函數的執行時長,對時長超過閾值的函數記錄一下 19 """ 20 def logger(duration, func=lambda name, duration: print('{} took {}s'.format(name, duration))): 21 def _logger(fn): 22 @copy_properties(fn) # wrapper = wrapper(fn)(wrapper) 23 def wrapper(*args,**kwargs): 24 start = datetime.datetime.now() 25 ret = fn(*args,**kwargs) 26 delta = (datetime.datetime.now() - start).total_seconds() 27 if delta > duration: 28 func(fn.__name__, duration) 29 return ret 30 return wrapper 31 return _logger 32 33 @logger(5) # add = logger(5)(add) 34 def add(x,y): 35 time.sleep(3) 36 return x + y 37 38 print(add(5, 6)) 39 40 41 42 #以上代碼輸出結果以下: 43 11
五.functools模塊
1>.functools概述
functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS,updated=WRAPPER_UPDATES) 相似copy_properties功能 wrapper 包裝函數、被更新者,wrapped 被包裝函數、數據源 元組WRAPPER_ASSIGNMENTS中是要被覆蓋的屬性'__module__', '__name__', '__qualname__', '__doc__', '__annotations__'模塊名、名稱、限定名、文檔、參數註解 元組WRAPPER_UPDATES中是要被更新的屬性,__dict__屬性字典 增長一個__wrapped__屬性,保留着wrapped函數
2>.functools模塊案例
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:y1053419035@qq.com 6 7 import datetime, time, functools 8 9 def logger(duration, func=lambda name, duration: print('{} took {}s'.format(name, duration))): 10 def _logger(fn): 11 @functools.wraps(fn) 12 def wrapper(*args,**kwargs): 13 start = datetime.datetime.now() 14 ret = fn(*args,**kwargs) 15 delta = (datetime.datetime.now() - start).total_seconds() 16 if delta > duration: 17 func(fn.__name__, duration) 18 return ret 19 return wrapper 20 return _logger 21 22 @logger(5) # add = logger(5)(add) 23 def add(x,y): 24 time.sleep(1) 25 return x + y 26 27 print(add(5, 6), add.__name__, add.__wrapped__, add.__dict__, sep='\n') 28 29 30 31 32 #以上代碼執行結果以下: 33 11 34 add 35 <function add at 0x0000000002A0F378> 36 {'__wrapped__': <function add at 0x0000000002A0F378>}