python的裝飾器其實就是乙個以函式作為引數並返回乙個替換函式的可執行函式。
裝飾器用到了閉包原理,目的是為了簡化**,用@來表示。
我們從乙個最簡單的裝飾器例子說起:
def
add_one
(f):
deffun
(*args):
a = args[0]
f(a + 1)
return fun
@add_one
defprint_self
(a):
print("a =", a)
print_self(1)
程式執行的結果為:
a =2
為什麼引數明明是1,卻輸出2呢?
關鍵就在於,因為有了裝飾器@add_one
的作用,print_self
已經不再是原來的print_self
了。
程式執行時,真正的步驟是:
1、在執行到print_self(1)
時,先將print_self
這個函式本身作為引數,傳入add_one
中。
2、然後會返回乙個函式fun
,相當於將print_self
裝飾成了fun
。
3、然後將引數 1 傳給fun
,於是args=(1,)
,然後有f(a+1)
,此時f
即為print_self
,因此執行的是print_self(a+1)
即print_self(2)
。
這個裝飾器,和下面的**是等效的:
#@add_one
defprint_self
(a):
print("a =", a)
print_self = add_one(print_self)
print_self(1)
其中,將裝飾器@注釋掉、且增加了一句手動呼叫add_one的語句,結果等效。
如果被裝飾函式沒有引數,則可以將裝飾器由閉包改為乙個普通的函式,但是也一定要返回乙個可呼叫函式:
def
add_one
(f):
print('excute add_one')
return f
@add_one
defprint_self
(): print("excute print_self")
print_self()
結果:
excute add_one
excute print_self
這種形式可以用於記錄呼叫函式的日誌。
上面的例子中,@add_one
後面是沒有引數的,但是add_one
函式本身是有乙個引數f
的,裝飾器會自動將所裝飾的函式print_self
傳給引數f
。但是有的裝飾器是如同@decorator(a, b)
這樣的形式,後面跟著引數,原理又是如何?
這時候,decorator(a, b)
其實不應該當做乙個裝飾器,而是乙個固定的函式值,我們需要計算出decorator(a, b)
,比如其返回乙個函式return_fun
,那麼這個返回的函式才是真正的裝飾器,相當於@retturn_fun
。
記住:@後面跟的永遠是乙個可呼叫的函式,而不是乙個固定的函式值,如果是乙個固定的函式值,那麼這個函式值本身,肯定是乙個可呼叫的函式。
舉例說明:
改造上面的add_one
裝飾器,我希望add的值不是固定為1,而是可以人為設定的,那麼可以將這個加數放到裝飾器中,如:
def
add_number
(num):
deffun
(f):
def_fun
(*args):
f(args[0] + num)
return _fun
return fun
@add_number(2)
defprint_b
(b):
print('b =', b)
print_b(1)
執行結果為:
b =3
執行原理:
1、執行print_b(1)
時,發現被@add_number(2)
裝飾。
2、計算add_number(2)
,返回fun
(此時裡面的num
已經固定為2)。
3、將print_b
當做引數f
傳給fun
,返回_fun
。
4、將引數1
當做引數*args
傳給_fun
,執行f(1 + 2)
。
5、輸出b = 3
。
如果被裝飾函式沒有引數,則可以將裝飾器由三層改為兩層,但是內層函式一定要返回乙個可呼叫函式,如:
def
add_number
(num):
deffun
(f):
print('num =', num)
return f
return fun
@add_number(2)
defprint_hello
(): print('hello')
print_hello()
結果:
num =2
hello
python裝飾器 python 裝飾器詳解
def outer x def inner y return x y return inner print outer 6 5 11 如 所示,在outer函式內,又定義了乙個inner函式,並且inner函式又引用了外部函式outer的變數x,這就是乙個閉包了。在輸出時,outer 6 5 第乙個...
python裝飾器詳解 python裝飾器詳解
按照 python 的程式設計原則,當乙個函式被定義後,如要修改或擴充套件其功能應盡量避免直接修改函式定義的 段,否則該函式在其他地方被呼叫時將無法正常執行。因此,當需要修改或擴充套件已被定義的函式的功能而不希望直接修改其 時,可以使用裝飾器。先來看乙個簡單的例子 def func1 functio...
詳解Python裝飾器
裝飾器的難點 在梳理了裝飾器的整個內容之後,我認為難點不是裝飾器本身,而是直接呼叫被裝飾的函式,讓人無法理解背後究竟發生了什麼。一 引出裝飾器概念 引入問題 定義了乙個函式,想在執行時動態的增加功能,又不想改動函式本身的 示例 希望對下列函式呼叫增加log功能,列印出函式呼叫 def f1 x re...