1、問題提出
希望在不修改原函數的情況下,對函數進行擴展。
2、問題解決
- 引入裝飾器
# 創建幾個函數
def add(a , b):
'''
求任意兩個數的和
'''
r = a + b
return r
def mul(a , b):
'''
求任意兩個數的積
'''
r = a * b
return r
# 希望函數可以在計算前,打印開始計算,計算結束後打印計算完畢
# 我們可以直接通過修改函數中的代碼來完成這個需求,但是會產生以下一些問題
# ① 如果要修改的函數過多,修改起來會比較麻煩
# ② 並且不方便後期的維護
# ③ 並且這樣做會違反開閉原則(OCP)
# OCP:程序的設計,要求開放對程序的擴展,要關閉對程序的修改
r = add(123,456)
print(r)
在不修改原函數的情況下,來對函數進行擴展。只需要根據現有的函數,來創建一個新的函數
def fn():
print('我是fn函數....')
def fn2():
print('函數開始執行~~~')
fn()
print('函數執行結束~~~')
fn2()
此時就達到了對fn函數的一個擴展
對第一個加法示例進行擴展:
def new_add(a,b):
print('計算開始~~~')
r = add(a,b)
print('計算結束~~~')
return r
r = new_add(111,222)
print(r)
上述方式,完成在不修改源代碼的情況下對函數進行擴展。
但是,這種方式要求每擴展一個函數就要手動創建一個新的函數,實在是太麻煩了。
爲了解決這個問題,我們創建一個函數,讓這個函數可以自動的幫助我們生產函數。
- 使用裝飾器
每擴展一個函數就可以手動創建一個新的函數,但是這個方式實在是太麻煩了。爲解決這個問題,我們可以創建一個函數,讓這個函數可以自動的幫助我們生產函數。
接下來就介紹一下這個自動生成函數的函數怎麼寫:
因爲我們在擴展的時候,不同的函數可能攜帶的參數不同,類型不同或者數量不同導致每次都要修改新函數的類型,所以這裏介紹一下*args 這種通用的用法解決該問題。
def begin_end(old):
'''
用來對其他函數進行擴展,使其他函數可以在執行前打印開始執行,執行後打印執行結束
參數:
old 要擴展的函數對象
'''
# 創建一個新函數
def new_function(*args , **kwargs):
print('開始執行~~~~')
# 調用被擴展的函數
result = old(*args , **kwargs)
print('執行結束~~~~')
# 返回函數的執行結果
return result
# 返回新函數
return new_function
f = begin_end(fn)
f2 = begin_end(add)
f3 = begin_end(mul)
# r = f()
# r = f2(123,456)
r = f3(123,456)
print(r)
像begin_end()這種函數我們就稱它爲裝飾器(舊函數作爲一個參數傳進去,返回一個新函數)。
通過裝飾器,可以在不修改原來函數的情況下來對函數進行擴展。(在開發中,我們都是通過裝飾器來擴展函數功能的。)
- 裝飾器的典型用法
def fn3(old):
'''
用來對其他函數進行擴展,使其他函數可以在執行前打印開始執行,執行後打印執行結束
參數:
old 要擴展的函數對象
'''
# 創建一個新函數
def new_function(*args , **kwargs):
print('fn3裝飾~開始執行~~~~')
# 調用被擴展的函數
result = old(*args , **kwargs)
print('fn3裝飾~執行結束~~~~')
# 返回函數的執行結果
return result
# 返回新函數
return new_function
@fn3
@begin_end
def say_hello():
print('大家好~~~')
say_hello()
此處先裝飾了離函數最近的begin_end,又在外部裝飾了fn3裝飾器。如果調換了位置,裝飾順序就不同了。
總結:在定義函數時,可以通過@裝飾器,來使用指定的裝飾器,來裝飾當前的函數。
可以同時爲一個函數指定多個裝飾器,這樣函數將會按照從內向外的順序被裝飾 。
3、參考
裝飾器的使用 :https://developer.aliyun.com/article/750028?spm=a2c6h.12873639.0.0.5e3f65cah9iK90&groupCode=python