本章結構:
1.理解裝飾器的前提准備
2.裝飾器:無參/帶參的被裝飾函數,無參/帶參的裝飾函數
3.裝飾器的缺點
4.python3的內置裝飾器
5.本文參考
理解裝飾器的前提:1.所有東西都是對象(函數可以當做對象傳遞) 2.閉包
閉包的概念:
1)函數嵌套
2)內部函數使用外部函數的變量
3)外部函數的返回值為內部函數
下面寫一個最為簡單的閉包的例子:
1 def test(name): 2 def test_in(): 3 print(name) 4 return test_in 5 6 func = test('whyz') 7 func()
裝飾器的原型:
1 import time 2 def showtime(func): 3 def wrapper(): 4 start_time = time.time() 5 func() 6 end_time = time.time() 7 print('spend is {}'.format(end_time - start_time)) 8 9 return wrapper 10 11 def foo(): 12 print('foo..') 13 time.sleep(3) 14 15 foo = showtime(foo) 16 foo()
不帶參數的裝飾器:(裝飾器,被裝飾函數都不帶參數)
1 import time 2 def showtime(func): 3 def wrapper(): 4 start_time = time.time() 5 func() 6 end_time = time.time() 7 print('spend is {}'.format(end_time - start_time)) 8 9 return wrapper 10 11 @showtime #foo = showtime(foo) 12 def foo(): 13 print('foo..') 14 time.sleep(3) 15 16 @showtime #doo = showtime(doo) 17 def doo(): 18 print('doo..') 19 time.sleep(2) 20 21 foo() 22 doo()
帶參數的被裝飾的函數
1 import time 2 def showtime(func): 3 def wrapper(a, b): 4 start_time = time.time() 5 func(a,b) 6 end_time = time.time() 7 print('spend is {}'.format(end_time - start_time)) 8 9 return wrapper 10 11 @showtime #add = showtime(add) 12 def add(a, b): 13 print(a+b) 14 time.sleep(1) 15 16 @showtime #sub = showtime(sub) 17 def sub(a,b): 18 print(a-b) 19 time.sleep(1) 20 21 add(5,4) 22 sub(3,2)
帶參數的裝飾器(裝飾函數),
實際是對原有裝飾器的一個函數的封裝,並返回一個裝飾器(一個含有參數的閉包函數),
當使用@time_logger(3)調用的時候,Python能發現這一層封裝,並將參數傳遞到裝飾器的環境去
1 import time 2 def time_logger(flag = 0): 3 def showtime(func): 4 def wrapper(a, b): 5 start_time = time.time() 6 func(a,b) 7 end_time = time.time() 8 print('spend is {}'.format(end_time - start_time)) 9 10 if flag: 11 print('將此操作保留至日志') 12 13 return wrapper 14 15 return showtime 16 17 @time_logger(2) #得到閉包函數showtime,add = showtime(add) 18 def add(a, b): 19 print(a+b) 20 time.sleep(1) 21 22 add(3,4)
類裝飾器:一般依靠類內部的__call__方法
1 import time 2 class Foo(object): 3 def __init__(self, func): 4 self._func = func 5 6 def __call__(self): 7 start_time = time.time() 8 self._func() 9 end_time = time.time() 10 print('spend is {}'.format(end_time - start_time)) 11 12 @Foo #bar = Foo(bar) 13 def bar(): 14 print('bar..') 15 time.sleep(2) 16 17 bar()
使用裝飾器的缺點:
1.位置錯誤的代碼->不要在裝飾器之外添加邏輯功能
2.不能裝飾@staticmethod 或者 @classmethod已經裝飾過的方法
3.裝飾器會對原函數的元信息進行更改,比如函數的docstring,__name__,參數列表:
下面對裝飾器第第三個缺點進行剖析,
1 import time 2 def showtime(func): 3 def wrapper(): 4 start_time = time.time() 5 func() 6 end_time = time.time() 7 print('spend is {}'.format(end_time - start_time)) 8 9 return wrapper 10 11 @showtime #foo = showtime(foo) 12 def foo(): 13 print('foo..') 14 time.sleep(3) 15 16 def doo(): 17 print('doo..') 18 time.sleep(2) 19 20 print(foo.__name__) 21 print(doo.__name__)
結果為:
wrapper
doo
由此可以看出,裝飾器會對原函數的元信息進行更改,可以使用wraps,進行原函數信息的添加
注解:wraps本身也是一個裝飾器,他能把函數的元信息拷貝到裝飾器函數中使得裝飾器函數與原函數有一樣的元信息
以下是一個wraps的例子:
1 import time 2 from functools import wraps 3 def showtime(func): 4 5 @wraps(func) 6 def wrapper(): 7 start_time = time.time() 8 func() 9 end_time = time.time() 10 print('spend is {}'.format(end_time - start_time)) 11 12 return wrapper 13 14 @showtime #foo = showtime(foo) 15 def foo(): 16 print('foo..') 17 time.sleep(3) 18 19 def doo(): 20 print('doo..') 21 time.sleep(2) 22 23 print(foo.__name__) 24 print(doo.__name__)
結果為:
foo
doo
常用的內置裝飾器:1.staticmethod: 類似實現了靜態方法 注入以后,可以直接 : 類名.方法
2.property:經過property裝飾過的函數 不再是一個函數,而是一個property,類似實現get,set方法
1 @property 2 def width(self): 3 return self.__width 4 5 @width.setter 6 def width(self, newWidth): 7 self.__width = newWidth
3.classmethod: 與staticmethod很相似,貌似就只有這一點區別:
第一個參數需要是表示自身類的 cls 參數,
可以來調用類的屬性,類的方法,實例化對象等。
本文參考:
1.https://www.cnblogs.com/vamei/archive/2013/02/16/2820212.html
2.https://www.cnblogs.com/wupeiqi/articles/4980620.html
3.https://www.cnblogs.com/yuanchenqi/articles/5830025.html
4.https://blog.csdn.net/mdzzname/article/details/78702440