現在,我們已經明白了裝飾器的原理。接下來,我們還有很多事情需要搞清楚。比如:裝飾帶引數的函式、多個裝飾器同時裝飾乙個函式、帶引數的裝飾器和類裝飾器。
def foo(func): # 接收的引數是乙個函式名
def bar(x, y): # 這裡需要定義和被裝飾函式相同的引數
print("這裡是新功能...") # 新功能
func(x, y) # 被裝飾函式名和引數都有了,就能執行被裝飾函式了
return bar
# 定義乙個需要兩個引數的函式
@foo
def f1(x, y):
print("{}+{}={}".format(x, y, x+y))
# 呼叫被裝飾函式
f1(100, 200)
輸出:
這裡是新功能...
100+200=300
def foo1(func):
print("d1")
def inner1():
print("inner1")
return "{}".format(func())
return inner1
def foo2(func):
print("d2")
def inner2():
print("inner2")
return "{}".format(func())
return inner2
@foo1
@foo2
def f1():
return "hello andy"
# f1 = foo2(f1) ==> print("d2") ==> f1 = inner2
# f1 = foo1(f1) ==> print("d1") ==> f1 = foo1(inner2) ==> inner1
ret = f1() # 呼叫f1() ==> inner1() ==> inner2() ==>inner1()==>hello andy
print(ret)
被裝飾的函式可以帶引數,裝飾器同樣也可以帶引數。回頭看我們上面寫得那些裝飾器,它們預設把被裝飾的函式當成唯一的引數。但是呢,有時候我們需要為我們的裝飾器傳遞引數,這種情況下應該怎麼辦呢?
接下來,我們就一步步實現帶引數的裝飾器:
首先我們來回顧下上面的**:
def f1(func): # f1是我們定義的裝飾器函式,func是被裝飾的函式
def f2(*arg, **kwargs): # *args和**kwargs是被裝飾函式的引數
func(*arg, **kwargs)
return f2
從上面的**,我們發現了什麼?
我的裝飾器如果有引數的話,沒地方寫了…怎麼辦呢?
還是要使用閉包函式!
# 三層巢狀的函式1
def f1():
def f2():
name = "andy"
def f3():
print(name)
return f3
return f2
巢狀三層之後的函式呼叫:
f = f1() # f --> f2
ff = f() # ff --> f3
ff() # ff() --> f3() --> print(name) --> andy
注意:在內部函式f3
中能夠訪問到它外層函式f2
中定義的變數,當然也可以訪問到它最外層函式f1
中定義的變數。
# 三層巢狀的函式2
def f1():
name = "andy"
def f2():
def f3():
print(name)
return f3
return f2
呼叫:
f = f1() # f --> f2
ff = f() # ff --> f3
ff() # ff() --> f3() --> print(name) --> andy
好了,現在我們就可以實現我們的帶引數的裝飾器函式了:
# 帶引數的裝飾器需要定義乙個三層的巢狀函式
def d(name): # d是新新增的最外層函式,為我們原來的裝飾器傳遞引數,name就是我們要傳遞的函式
def f1(func): # f1是我們原來的裝飾器函式,func是被裝飾的函式
def f2(*arg, **kwargs): # f2是內部函式,*args和**kwargs是被裝飾函式的引數
print(name) # 使用裝飾器函式的引數
func(*arg, **kwargs) # 呼叫被裝飾的函式
return f2
return f1
上面就是乙個帶參裝飾器的**示例,現在我們來寫乙個完整的應用:
def d(a=none): # 定義乙個外層函式,給裝飾器傳引數--role
def foo(func): # foo是我們原來的裝飾器函式,func是被裝飾的函式
def bar(*args, **kwargs): # args和kwargs是被裝飾器函式的引數
# 根據裝飾器的引數做一些邏輯判斷
if a:
print("歡迎來到{}頁面。".format(a))
else:
print("歡迎來到首頁。")
# 呼叫被裝飾的函式,接收引數args和kwargs
func(*args, **kwargs)
return bar
return foo
@d() # 不給裝飾器傳引數,使用預設的'none'引數
def index(name):
print("hello {}.".format(name))
@d("電影") # 給裝飾器傳乙個'電影'引數
def movie(name):
print("hello {}.".format(name))
if __name__ == '__main__':
index("andy")
movie("andy")
輸出:
歡迎來到首頁。
hello andy.
歡迎來到電影頁面。
hello andy.
除了用函式去裝飾函式外,我們還可以使用類去裝飾函式。
class d(object):
def __init__(self, a=none):
self.a = a
self.mode = "裝飾"
def __call__(self, *args, **kwargs):
if self.mode == "裝飾":
self.func = args[0] # 預設第乙個引數是被裝飾的函式
self.mode = "呼叫"
return self
# 當self.mode == "呼叫"時,執行下面的**(也就是呼叫使用類裝飾的函式時執行)
if self.a:
print("歡迎來到{}頁面。".format(self.a))
else:
print("歡迎來到首頁。")
self.func(*args, **kwargs)
@d()
def index(name):
print("hello {}.".format(name))
@d("電影")
def movie(name):
print("hello {}.".format(name))
if __name__ == '__main__':
index("andy")
movie("andy")
我們上面所有的例子都是裝飾乙個函式,返回乙個可執行函式。python中的裝飾器除了能裝飾函式外,還能裝飾類。
可以使用裝飾器,來批量修改被裝飾類的某些方法:
# 定義乙個類裝飾器
class d(object):
def __call__(self, cls):
class inner(cls):
# 重寫被裝飾類的f方法
def f(self):
print("hello andy.")
return inner
@d()
class c(object): # 被裝飾的類
# 有乙個例項方法
def f(self):
print("hello world.")
if __name__ == '__main__':
c = c()
c.f()
整理Python裝飾器
需要理解的三個方面 1 函式即 變數 2 高階函式 a 把函式名當做實參傳給另乙個函式 在不修改被裝飾函式源 的情況下進行功能擴充套件 b 返回值中包含函式名 不修改函式的呼叫方式 3 巢狀函式 函式裡面用def宣告另乙個函式 高階函式 巢狀函式 裝飾器 一 未使用裝飾器 usr bin env p...
python基礎知識整理 裝飾器
最簡裝飾器 def deco func def wrap args,kwargs return func args,kwargs return wrap deco def foo a,b return a b原理 對比被裝飾前後的foo.name 和foo.doc from functools im...
裝飾器之類裝飾器
外部的方法至今都玩過了,現在來思索一下的方法這麼裝飾 類方法修飾器 類的方法唯一的特殊點在於,self內部是可以呼叫的,但是在外部卻是隱藏的,那這個怎麼搞 為求穩妥,先定參修飾乙個 def godme fun def godme self,message print before fun self,...