由於軟體的設計遵循開發封閉原則(對於擴充套件開發,對於程式修改封閉)。所以對於軟體的擴充套件是對軟體二次開發的最好途徑。這時候就需要使用到裝飾器這個概念了。
裝飾器就是為裝飾的物件新增新的功能,並且是在不修改原始碼的情況下,還必須使得在外行看起來沒有發生任何變化(呼叫方法、軟體實現主要功能……)。
裝飾器分為無參裝飾器和有參裝飾器,裝飾器的實現都是通過「函式巢狀+閉包+函式物件」組合生成的。
def outter
(func)
: # 傳進來的是被裝飾的函式的物件
(*args,
**kwargs)
: res =
func
(*args,
**kwargs)
return res
@outter
def index
(x,y)
:print
(x,y)
import time
# 定義乙個小函式
def index()
: time.
sleeep
(1.5
)print
("welcome to index page!!!"
)return
100index
() # 呼叫函式
如果需要在這個函式呼叫時候新增乙個功能:就是實現輸出呼叫函式需要使用的時間,雖然說這個功能實現很簡單,並沒有什麼複雜的,但是如果只是使用下面的這種解法,那麼就太老土了。
解法一:(老土、麻煩)
start = time.
time()
index
() # 呼叫函式
print
("用時:"
,time.
time()
-start)
這樣子對於只是乙個兩個函式的時候,就能夠簡單實現,但是如果很多的函式呼叫都需要輸出時間的時候就會很麻煩了,**量也會變得很累贅了。
解法二:(呼叫方式改變了,不是很合要求)
def timer
(fun)
: start = time.
time()
fun(
) # 呼叫函式
print
("用時:"
,time.
time()
-start)
timer
(index) # 呼叫乙個新的函式,實現所需要的功能
這個方法是對第乙個方法的改進,減少了**量,但是同時也帶來了乙個缺點,那就是改變了呼叫方式,這樣子好像不太滿足要求了。
解法三:(使用裝飾器)
import time
def timer
(func):(
): # 引用外部作用域的變數func
start_time=time.
time()
res=
func()
stop_time=time.
time()
print
('run time is %s'
%(stop_time-start_time)
)return res
# 定義乙個小函式
@ timer # 新增裝飾器物件
def index()
: time.
sleeep
(1.5
)print
("welcome to index page!!!"
)return
100# 這時候的呼叫方式
index
() # 呼叫函式,但是這時候呼叫的函式就不再是原先的index函式了,而是加了裝飾器的index函式
使用裝飾器之後,呼叫方式沒有發生任何的改變,同時也實現了附加的功能;同時如果還有其他的函式想要實現這個種附加的功能也這需要新增乙個裝飾器就可以了。
由於語法糖 @ 的限制,outter函式只能有乙個引數,並且這才是只用來接受被裝飾物件的記憶體位址
# 定義乙個驗證功能的裝飾器
defauth
(driver)
:# 最高層傳遞引數(第三層)
defdeco
(func)
:def
(*args,
**kwargs)
: name =
input
("input user>>"
).strip(
) pwd =
input
("input pwd>>"
).strip(
)if driver ==
'file'
:print
("基於檔案驗證"
)# 編寫基於檔案的認證,認證通過則執行res=func(*args,**kwargs),並返回res
# 開啟檔案,傳檔案中讀取使用者資訊進行匹配
if name ==
"xiaoming"
and pwd ==
"123456"
:print
("基於檔案驗證成功"
)else
:print
("基於檔案驗證失敗"
)elif driver ==
'mysql'
:# 編寫基於mysql認證,認證通過則執行res=func(*args,**kwargs),並返回res
print
("基於資料庫驗證"
)if name ==
"xiaoming"
and pwd ==
"123456"
:print
("基於資料庫驗證成功"
)else
:print
("基於資料庫驗證失敗"
)else
:print
("傳入驗證引數有誤"
) return deco
# auth(driver="file") ----- 執行之後返回乙個deco函式的記憶體位址;(新增這一層閉包的主要功能就是為了傳遞引數)
# @deco 這個就是一般的裝飾器語法糖(乙個兩層的閉包函式)
@auth(driver=
"file"
)def
index
(x,y)
:print
("index{}{}"
.format
(x,y)
)@auth(driver=
"mysql"
)def
home
(x,y)
:print
("index{}{}"
.format
(x,y)
)index(2,
6)home(5,
9)
綜合以上操作就實現了有參裝飾器的傳遞,但是還存在乙個問題,就是雖然有參裝飾器是實現了,並且呼叫方式都沒發生任何的變化,但是還有乙個問題,那就是函式的屬性以及一些其他的附加內容,並沒有進行修改,這時候其實需要把他們全部進行修改才是乙個完美的裝飾器。 函式裝飾器 類裝飾器
一 函式裝飾函式 defwrapfun func definner a,b print function name func.name r func a,b return r return inner wrapfun defmyadd a,b return a b print myadd 2,3 二...
python裝飾器 函式裝飾器,類裝飾器
只要實現此 模式,這個obj就叫乙個裝飾器 參考 函式裝飾器 例子 def decorator func def inner args,kwargs print before.res func args,kwargs print after.return res return inner decor...
python 裝飾器 函式裝飾器 類裝飾器
python函式裝飾器和類裝飾器筆記.usr bin env python coding utf 8 author ivan file decorators.py version from functools import wraps 裝飾器 目的是為了給函式新增附加功能 1.不帶引數裝飾器 此方式...