今天我們來聊一聊裝飾器,什麼是裝飾器呢?
舉個例子:早上你一絲不掛的起來,想今天要穿什麼衣服來裝扮一下自己。這裡衣服就相當於裝飾器,而你赤裸裸的身體就是要被裝飾的函式或者是類。
函式裝飾器
python裝飾器是非常不錯的特性,熟練掌握裝飾器會讓你的程式設計思路更加寬廣,程式也更加pythonic。下面就讓我們一起來**一下python的裝飾器吧。
裝飾器的存在是為了適用兩個場景,乙個是增強被裝飾函式的行為,另乙個是**重用。
先看乙個例子,直觀的感受一下:
1 importtime2
7 start_time =time.time()8
9 func()10
11 stop_time =time.time()12
13 print('used time {}'.format(stop_time-start_time))14
19 deftest1():20
21 time.sleep(1)22
23 print('i am test1!')
輸出:1 i am test1!2 used time 1.0000572204589844
1、不能修改被裝飾函式的**;
2、不能修改被裝飾函式的呼叫方式;
這並不難以理解,因為在生產環境中如果我們要給某個函式新增功能,最好不要修改該函式的原始碼,因為可能造成意想不到的影響,或者這個**是乙個大神寫的,你根本不知從何改起;同時你也不能修改其呼叫方式,因為你不知道程式中有多少地方呼叫了此函式。
那麼我們從函式和函式名說起吧。
1 deffunc(name):2
3 print('i am {}!'.format(name))4
5 func('li')6
7 y =func8
9 y('liu')
輸出:1 i am li!2 i am liu!
定義函式func,呼叫函式func,將函式名func賦值給y,呼叫y。y=func 表明:函式名可以賦值給變數,並且並不影響呼叫。
這其實和整數、數字是一樣的:
1 a = 1
2 b =a
3 print(a, b)
明白了這一點,下面再說說高階函式: 高階函式滿足如下兩個條件中的任意乙個: a. 可以接收函式名作為實參; b. b.返回值中可以包含函式名;
其實python標準庫中的map和filter等函式就是高階函式。
1 l = [1, 2, 4]2
3 r = map(lambda x: x*3, l)4
5 for i inr:6
7 print(i)
自定義乙個能返回函式的函式,也是高階函式
1 deff(l):2
3 return map(lambda x: x*5, l)4
5 a =f(l)6
7 for i ina:8
9 print(i)
有了這些基礎,我們就可以嘗試實現一下類似裝飾器的功能了。
1 defout(func):2
3 print('add a function.')4
5 returnfunc6
7 deftest1():8
9 time.sleep(1)10
11 print('i am test1!')12
13 temp =out(test1)14
15 temp()
輸出:1 add a function.
2 i am test1!
還是第乙個例子中的test1函式,我們定義了乙個函式out,out接收乙個函式名然後直接返回該函式名。這樣,我們實現了不修改原函式test1,並且新增了乙個新功能的需求,但是缺陷就是呼叫方式改變了。如何解決這個問題呢?其實很簡單,相信 a = a * 3 這樣的表示式我們都見過,那麼上述**中的temp = out(test1) 同樣可以修改為 test1 = out(test1),這樣我們就完美的解決了問題:既新增了新功能又沒有修改原函式和其呼叫方式。修改後的**如下:
1 defout(func):2
3 print('add a function.')4
5 returnfunc6
7 deftest1():8
9 time.sleep(1)10
11 print('i am test1!')12
13 test1 =out(test1)14
15 test1()
只是美中不足的事每次需要使用裝飾器的時候,都要在寫一句類似test1 = out(test1) 的**。python為了簡化這種情況,提供了乙個語法糖@,在每個被裝飾的函式上方使用這個語法糖就可以省掉這一句**test1 = out(test1)。如下:
1 defout(func):2
3 print('add a function.')4
5 returnfunc6
7 @out8
9 deftest1():10
11 time.sleep(1)12
13 print('i am test1!')14
15 #test1 = out(test1)
17 test1()
至此,我們搞清楚了裝飾器的工作原理,但是對比開篇的例子,還是有些不一樣。這又是為什麼呢? 開篇例子實現的是輸出被裝飾函式的執行時間,那麼必須在函式執行之前記錄一下時間,函式執行之後記錄一下時間,這樣才能計算出函式的執行時間,但是我們現在是直接返回了函式名,這樣函式呼叫後我們就沒辦法做任何事情了,所以此時我們需要在巢狀一層函式,將實現額外功能的部分寫在內層函式中,然後將這個內層函式返回即可。這也是為什麼裝飾器都是巢狀函式的原因。 另外,開篇的例子並沒有返回值,也沒有引數,要對既有引數又有返回值的函式進行裝飾的話,還需要進一步完善。 能夠處理返回值的裝飾器:
1 importtime2
7 start_time =time.time()8
9 result =func()10
11 stop_time =time.time()12
13 print('used time {}'.format(stop_time -start_time))14
15 returnresult16
21 deftest1():22
23 time.sleep(1)24
25 print('i am !')26
27 return 'test1 return'
29 x =test1()30
31 print(x)
輸出:1 i am !2 used time 1.0000572204589844
3 test1 return
能夠處理引數的裝飾器:
5 start_time =time.time()6
7 result = func(*args, **kwargs)8
9 stop_time =time.time()10
11 print('used time {}'.format(stop_time -start_time))12
13 returnresult14
19 deftest1(args):20
21 time.sleep(1)22
23 print('i am {}!'.format(args))24
25 return 'test1 return'
27 x = test1('li')28
29 y = test1('liu')30
31 print(x, y)
輸出:1 i am li!2 used time 1.0000569820404053
3 i am liu!4 used time 1.0000572204589844
5 test1 return test1 return
總結:裝飾器的本質是函式,其引數是另乙個函式(被裝飾的函式)。 裝飾器通常會額外處理被裝飾的函式,然後把它返回,或者將其替換成另乙個函式或可呼叫物件。行為良好的裝飾器可以重用,以減少**量。
python專題 python基礎
這個就基礎了,變數和常量的主要最為本質的原因是記憶體中,該塊記憶體的讀寫許可權,常量是不可以被重新賦值的,變數是可以被重新賦值。換句話說,變數的記憶體是可以被讀寫的,而常量的記憶體許可權僅僅是唯讀。python中的識別符號和其他語言都基本一樣,只能由字母,數字,下劃線組成,數字不能開頭!python...
Python學習 Python函式
函式的作用 封裝功能,方便呼叫 函式的定義 定義 def 函式名 引數列表 語句return 表示式 解釋 def 函式關鍵字,表明定義乙個函式 函式名 遵守識別符號規則 引數列表的開始和結束,如果沒有引數也不可以省略 引數列表 函式的呼叫者給函式傳遞的資料 語句 封裝的功能 return 用於結束...
python學習 python緒論
在命令提示符視窗輸入python,就可以執行python啦!如果在互動式環境輸入python後出現 python is not recognized as an internal or external command,operable program or batch file.是因為window...