今天其實也有人問到了python裝飾器是什么,一下子我也被問得有點懵逼了,有些基礎的理論確實忘了,然而因為也是自己寫的代碼,也沒有去做所謂的核心代碼的區別,但是重新看了一下理論,還是知道了大概的一個作用是什么。
Python裝飾器就是用於拓展原來函數的一種函數,在不改動原函數的代碼的前提下給函數增加新的功能,這也是代碼可拓展性保證了核心代碼不被破壞的重要函數。而這個函數的特殊之處也是在於他的返回值也是一個函數。
一般來說,想要拓展原來的函數代碼,最直接的辦法就是修改原先的代碼,傾入原先的代碼進行修改。
例如:這個是個簡單輸出hello world的一個函數
import time def func(): print("hello") time.sleep(1) print("world")
現在的需求是,我們要記錄一下,這個函數執行的總時間,當然最簡單的方法就是:
在原有的代碼上去加一些代碼條件,包括去記錄開始時間和結束時間,以此來計算出一個總時間。
import time def func(): startTime = time.time() print("hello") time.sleep(1) print("world") endTime = time.time() msecs = (endTime - startTime)*1000 print("time is %d ms" %msecs)
這樣一來是不是比較簡單,但是別慌啊,好戲在后頭。在一些公司,可能這個代碼不是你寫的,但是現在好比說這個代碼,主管(BOSS)跟你說,這段代碼是核心代碼,不能直接改核心代碼,想辦法去增加功能!
這個時候,裝飾器就派上用場了,現在,我自己重新加了一個裝飾器去寫
我們先寫一個簡單的裝飾器
import time def deco(func): def wrapper(): start_time = time.time() func() end_time = time.time() timer = end_time - start_time print("run time spend :", timer, 's', sep='') return wrapper def func(): print("hello") time.sleep(1) print("world") func = deco(func) func()
上面這個例子中,其實deco就是一個裝飾器,它把執行業務的func()包汲取了,看起來像是被裝飾了一樣。
在這個例子中,函數進入和退出時,被成為一個橫切面,這種編程通常被成為面向切面的變成(Aspect-Oriented Programming)簡稱AOP。
和面向對象編程不同的是。
這樣的做法,對原有代碼毫無入侵性,這就是AOP的好處了,把和主業務無關的事情,放到代碼外面去做。
@符號是裝飾器的語法糖,在定義函數的時候使用,避免再一次賦值操作
有些人會不明白了,語法糖又是什么?
語法糖(Syntactic sugar),也譯為糖衣語法,是由英國計算機科學家彼得·約翰·蘭達(Peter J. Landin)發明的一個術語,指計算機語言中添加的某種語法,這種語法對語言的功能並沒有影響,但是更方便程序員使用。通常來說使用語法糖能夠增加程序的可讀性,從而減少程序代碼出錯的機會。
簡單的來說,就是提升效率,甚至可以帶來更簡潔的效果。
import time def deco(func): def wrapper(): start_time = time.time() func() end_time = time.time() timer = end_time - start_time print("run time spend :", timer, 's', sep='') return wrapper @deco def func(): print("hello") time.sleep(1) print("world") func()
可以看到,我們定義了一個函數deco,然后它的參數是一個函數,然后增了一個計時的功能。
有些人可能不明白,這個@deco是什么意思呢?
其實@deco 就是引用裝飾器,相當於func = deco(func),是的,然后deco其實就是一個裝飾器,也就是語法糖
@deco 在編譯器的意思就等同於func = deco(func)
這樣的話效率提升,簡潔有效!
當然拉,語法糖不是強制要求的,目的就是為了書寫簡單方便。
而這里調用的方式其實還是跟之前一樣。
裝飾器的意義:裝飾器在不改變被裝飾函數的源代碼和調用方式的情況下增加新的功能,所以即便是修改了原先的調用方式也是不行。
這樣就可以在不動用核心代碼的基礎上去拓展它的函數功能!
同時,裝飾器其實也是可以帶入參數的。
在上一個列子中,我們已經加了一個裝飾器了,那么怎樣才可以帶入參數呢?
單參數的例子:
import time def use_parameter(par): def deco(func): def wrapper(): start_time = time.time() func() end_time = time.time() timer = end_time - start_time if par == True: print("run time spend :", timer, 's', sep='') return wrapper return deco @use_parameter(True) def func(): print("hello") time.sleep(1) print("world") func()
雙參數是以此類推,要記得,由於use_parameter里面是有兩個函數的,所以兩個函數都要返回相應的函數,否則就會出現報錯。
還有就是類的裝飾,相比函數裝飾器,類裝飾器具有靈活度大,高內聚、封裝性等有點。使用類裝飾器還可以依靠內部的一些方法。
import time class Test(object): def __init__(self, func): self.func = func def __call__(self, *args, **kwargs): start_time = time.time() self.func() end_time = time.time() timer = end_time - start_time print("run time spend :", timer, 's', sep='') @Test def func(): print("hello") time.sleep(1) print("world") func()
用類封裝裝飾器的例子中看出,func已經進入了類Test的_call_的方法,init方法中記錄傳入的函數,然后使用call調用修飾的函數和其他的處理,這里的call也等同於之前例子中的deco()函數,當然還有使用對象作為裝飾器的做法也是有的,更方便的定制和添加參數
