一、裝飾器如何使用
裝飾器是用來“打扮函數”的,在程序開發中常常使用到裝飾器,它使得開發效率更高,方便對代碼進行擴展。一般我們寫代碼時遵循開放封閉原則,它規定已經實現的功能代碼不允許被修改,但可以被擴展。
封閉: 已經實現的功能代碼塊不允許修改
開放:對擴展功能進行開發
"""現在需要對下面三個模塊進行功能擴展,在原來的基礎上加入驗證功能"""
def test1():
print(‘--test1--’)
def test2():
print('--test2--')
def test3():
print('--test3--')
如果沒有裝飾器,那麼傳統的做法是先定義一個驗證模塊,然後每個模塊進行調用即可。下面看代碼
def verify():
print("正在驗證......")
if "條件":
print("驗證成功!")
else:
print("驗證失敗,請確認!")
def test1():
verify()
print(‘--test1--’)
def test2():
verify()
print('--test2--')
def test3():
verify()
print('--test3--')
貌似利用上面的方法也可以實現。但是我們發現對原來的模塊進行了修改,違反了“開放封閉原則”。接下來採用一種方法來避免這種情況。就是利用閉包的手段來避免對原來模塊進行修改。
“”“驗證模塊設計”“”
def verify(func):
def inner():
print("正在驗證......")
if "條件":
print("驗證成功!")
func() # 執行調用的函數
else:
print("驗證失敗,請確認!")
return inner # 返回函數inner()的引用
def test1():
print(‘--test1--’)
def test2():
print('--test2--')
def test3():
print('--test3--')
# 下面分別爲函數test1()、test2()、test3()添加驗證功能
inner_fun = verify(test1) # 這裏的test1參數是將函數test1的引用給了參數func。
inner_fun() # 執行函數,上面參數傳入那個函數,調用內部函數時就執行相應的函數模塊。
# test2()中加入驗證功能
inner_fun = verify(test2) # 這裏的test2參數是將函數test2的引用給了參數func。
inner_fun() # 執行函數,上面參數傳入那個函數,調用內部函數時就執行相應的函數模塊
#test3()中加入驗證功能
inner_fun = verify(test2) # 這裏的test2參數是將函數test2的引用給了參數func。
inner_fun() # 執行函數,上面參數傳入那個函數,調用內部函數時就執行相應的函數模塊
利用閉包的方法似乎解決了上面對原來函數進行修改的問題。這樣滿足了開放封閉原則,但是似乎函數寫的不夠優美。下面利用裝飾器來簡化上述代碼。
“”“驗證模塊設計”“”
def verify(func):
def inner():
print("正在驗證......")
if "條件":
print("驗證成功!")
func() # 執行調用的函數
else:
print("驗證失敗,請確認!")
return inner # 返回函數inner()的引用
@verify # 等價於:inner_fun = verify(test1)
def test1():
print(‘--test1--’)
@verify # 等價於:inner_fun = verify(test2)
def test2():
print('--test2--')
@verify # 等價於:inner_fun = verify(test3)
def test3():
print('--test3--')
"""可以看到將上面代碼分別加入了@verify,這一句代碼表示的意思等價於: inner_fun = verify(test1)"""
那麼裝飾器函數在什麼時候進行裝飾? 只要上面一段代碼寫了@verify後,執行上面的代碼後,就進行了裝飾。我們會發現上面函數只是定義好了,但是還沒有調用函數怎麼會執行呢?這就時裝飾器的一個特點,只要定義了裝飾器 ,那麼在調用函數之前就進行了裝飾,而不是等到調用函數時才進行裝飾,這一點要清楚。
下面對函數進行調用:
# 調用test1
test1()
# 調用test2
test2()
# 調用test3
test3()
“”“其實我們發現比之前不用裝飾器時代碼更加簡潔。可以回過頭對比下之前沒用裝飾器時的代碼。””“
二、兩個裝飾器函數
# 定義裝飾函數
def makeBold(fn):
def wrapped():
return "<b>" + fn() + "</b>"
return wrapped
# 定義裝飾函數
def makeItalic(fn):
def wrapped():
return "<i>" + fn() + "</i>"
return wrapped
# @ makeBold 等價於 fn = makeBold(test)
@makeBold # 後裝飾,先調用(執行)
# @ makeItalic 等價於 fn = makeItalic(test)
@makeItalic # 先裝飾,後調用 (靠近函數的先裝飾,後調用)
def test():
return "--兩個裝飾函數共同裝飾--"
# 調用函數test
test()
輸出結果:
<b><i> --兩個裝飾函數共同裝飾--<i><b>
三、帶有參數的裝飾器
裝飾器帶有參數,在原有的裝飾器的基礎上,設置外部變量。
# 下面的例子是裝飾器帶有參數的情況
from time import ctime,sleep
def timefun_arg(parse):
def timefun(func):
def wrappedfun():
print("%s called %s %s"% (func.__name__,ctime(),parse))
return func
return wrappedfun
return timefun
@timefun_arg("test")
“”“先調用timefun_arg()這個函數,函數的返回值作爲裝飾,即timefun, 這裏相當於@ timefun,與上面講過的裝飾器是一樣的,只不過多了傳參數的這一步。”“”
def test1():
print("---test1---")
@timefun_arg("python")
def test2():
print("---test2---")
test1() # 輸出結果:test1 called Mon Feb 10 08:48:00 2020 test
sleep(2)
test2() # 輸出結果:test2 called Mon Feb 10 08:48:02 2020 python
那麼上面帶參數的裝飾器有什麼作用呢?如果我們只有一個裝飾器,讓它根據不同的參數進行特定的判斷,或者其他操作。將上面的列子加上判斷後看看效果。
from time import ctime,sleep
def timefun_arg(parse):
def timefun(func):
def wrappedfun():
if parse == "Python":
print("This is %s"%parse)
else:
print("Others")
return func
return wrappedfun
return timefun
@timefun_arg("test")
“”“先調用timefun_arg()這個函數,函數的返回值作爲裝飾,即timefun, 這裏相當於@ timefun,與上面講過的裝飾器是一樣的,只不過多了傳參數的這一步。”“”
def test1():
print("---test1---")
@timefun_arg("Python")
def test2():
print("---test2---")
# 調用函數
test1() # 輸出結果:Others
sleep(2)
test2() # 輸出結果:This is Python
“”“可以通俗的說,帶參數的迭代器可以根據不同參數進行特定的作用!”“”