Python學習筆記(十):函式裝飾器
關于函式裝飾器的詳細理解
- Python學習筆記(十):函式裝飾器
- 一.函式裝飾器
- 二.裝飾器機制分析
- 三.帶引數的函式裝飾器
- 四.函式裝飾器的嵌套
一.函式裝飾器
拓展原來函式功能的一種函式,它的回傳值也是一個函式
在不修改原有代碼的情況下,為被裝飾的物件增加新的功能或者附加限制條件或者幫助輸出
二.裝飾器機制分析
Python程式是從上往下順序執行的,碰到函式的定義代碼塊不會立即執行,只有等到該函式被呼叫時,才會執行其內部的代碼塊
對同一個函式定義了兩次,后面的定義會覆寫前面的定義
def outer(func):
def inner():
print("內層函式!")
return inner
def foo():
print("原始函式!")
outer(foo)
outer(foo())
函式名: foo、outer、inner
函式體:函式的整個代碼結構
回傳值: return后面的運算式
函式的記憶體地址:id(foo)、id(outer)等等
函式名加括號:對函式進行呼叫,比如foo()、outer(foo)
函式名作為引數: outer(foo)中的foo本身是個函式,作為引數被傳遞給了outer函式
函式名加括號被當做引數:先呼叫函式,再將它的回傳值當做別的函式的引數,例如outer(foo())
回傳函式名:return inner
回傳函式名加括號:return inner(),先執行inner函式,再將其回傳值作為別的函式的回傳值,
def funA(fn): #fnA 作為裝飾器函式
fn() # 執行傳入的fn引數
return '...'
@funA
def funB():
使用函式裝飾器 funA() 去裝飾另一個函式 funB()
其底層將 funB 作為引數傳給 funA() 函式;
將 funA() 函式執行完成的回傳值反饋回 funB()
def funA(fn):
fn() # 執行傳入的fn引數
return '...'
def funB():
funB = funA(funB)
def funA(fn): #funA 作為裝飾器函式
print("youchanwill")
fn() # 執行傳入的fn引數
print("20")
return "裝飾器函式的回傳值"
@funA
def funB():
print("you")
print(funB)
youchanwill
you
20
裝飾器函式的回傳值
被“@函式”修飾的函式不再是原來的函式,而是一個新的東西(取決于裝飾器的回傳值)
如果裝飾器函式的回傳值為普通變數,那么被修飾的函式名就變成了變數名
如果裝飾器回傳是一個函式的名稱,那么被修飾的函式名依然表示一個函式
def f1():
print("基礎函式")
def f1():
#加入函式呼叫的安全認證
print("基礎函式")
def login(): #定義認證函式
print("認證函式")
def f1():
login() #定義認證函式,在其他函式中呼叫
print("基礎函式")
代碼的開放封閉原則,已經實作的功能代碼內部不允許被修改,但外部可以被擴展,就是裝飾器的作用
def outer(func):
def inner():
print("認證函式")
result = func()
print("添加日志")
return result
return inner
@outer #@outer和@outer()有區別,沒有括號時,outer函式依然會被執行
def f1():
print("基礎函式")
1. 程式讀到def outer(func):時,是把函式體加載到記憶體里
2. 到@outer時,知道這是裝飾器,于是開始運行@后面outer所定義的函式
3. 回傳到outer函式,執行裝飾器的語法規則
規則
-
被裝飾的函式的名字會被當作引數傳遞給裝飾函式
-
裝飾函式執行它自己內部的代碼后,會將它的回傳值賦值給被裝飾的函式
-
原來的f1()函式被當做引數傳遞給了func,f1這個函式名之后會指向inner函式
*函式名f1(不是被呼叫后)當做引數傳遞給裝飾函式outer,func = f1,@outer = outer(f1),傳遞了f1()的函式體,而不是執行f1()后的回傳值
*outer函式return的是inner這個函式名,而不是inner()被呼叫后的回傳值
4.程式開始執行outer函式內部的內容,到函式inner時,inner函式定義塊不會立刻執行,而是讀入記憶體中(默認規則)
5.到return inner,回傳值是函式名,并且這個函式名會被賦值給f1這個被裝飾的函式,也就是f1 = inner
6.f1函式被新的函式inner覆寫了(f1這個函式名更改成指向inner這個函式名指向的函式體記憶體地址),再呼叫f1的時候將執行inner函式內的代碼,而不是先前的函式體
7.原來的f1()函式被當做引數傳遞給了func,func這個變數保存了老的函式在記憶體中的地址,在inner函式里的result = func()可以執行老的函式體
8.當通過f1()的方式呼叫f1函式時,執行的就不再是舊的f1函式的代碼,而是inner函式的代碼
9.在本例中,首先會print“認證成功”,然后執行func函式并將回傳值賦值給變數result,func函式就是舊的f1函式
10.接著print“日志保存”,回傳result這個變數,可以用r = f1()的方式接受result的值
11.在函式呼叫前進行認證,呼叫后寫入日志
def outer(func):
def inner():
print("認證函式")
result = func()
print("添加日志")
return result
return inner
@outer #@outer和@outer()有區別,沒有括號時,outer函式依然會被執行
def f1():
print("基礎函式")
pass
r = f1() #呼叫函式
認證函式
基礎函式
添加日志
1.執行到裝飾器,將裝飾器下方函式名傳入裝飾器作為引數,func = f1
2.inner()寫入記憶體,return result回傳inner,將inner賦值給f1,f1 = inner
3.r = f1(),這時f1() = inner(),那么執行def inner():,輸出print("認證函式")
4.執行result = func(),fun = f1,那么這里執行def f1():,輸出print("基礎函式")
5.執行print("添加日志"),執行return result回傳result原來的結果
def outer(func):
print("認證函式")
result = func()
print("添加日志")
return result
@outer
def f1():
print("基礎函式")
認證函式
基礎函式
添加日志
在一層函式下,當執行到裝飾器的時候就會立刻執行outer函式中的內容,如果不封裝,在函式還未呼叫的時候,就執行了
三.帶引數的函式裝飾器
如果被修飾的函式本身帶有引數,那么在函式裝飾器中嵌套一個函式,該函式帶有的引數個數和被裝飾器修飾的函式相同
def funA(fn): # 定義嵌套函式
def say(name):
print("you",name)
return say
@funA
def funB(name):
print("funB():", a)
funB("chanwill")
you chanwill
通過funB()函式被裝飾器funA()修飾,funB 被賦值為 say,雖然呼叫的是 funB() 函式,但實際執行的是say()函式
def funA(fn): # 定義一個嵌套函式
def say(name):
print("you",name)
return say
def funB(name):
print("funB():", a)
funB = funA(funB)
funB("chanwill")
當前程式中,有多個函式被同一個裝飾器函式修飾,這些函式帶有的引數個數并不相等
可用 *args 和 **kwargs 作為裝飾器內部嵌套函式的引數
*args 和 **kwargs 表示接受任意數量和型別的引數
def funA(fn): # 定義一個嵌套函式
def say(*args,**kwargs):
fn(*args,**kwargs)
return say
@funA
def funB(name):
print("you",name)
@funA
def funC(age,name):
print(age,name)
funB("chanwill")
funC("20","CHANWILL")
you chanwill
20 CHANWILL
四.函式裝飾器的嵌套
Python支持多個裝飾器
@funA
@funB
@funC
def fun():
#...
程式的執行順序是從里到外,等效于下面這行代碼
fun = funA( funB ( funC (fun) ) )
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/258954.html
標籤:python
