裝飾器進階
如今,咱們已經明白了裝飾器的原理。接下來,咱們還有不少事情須要搞清楚。好比:裝飾帶參數的函數、多個裝飾器同時裝飾一個函數、帶參數的裝飾器和類裝飾器。python
裝飾帶參數函數
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 "<i>{}</i>".format(func())
return inner1
def foo2(func):
print("d2")
def inner2():
print("inner2")
return "<b>{}</b>".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() ==> <i>inner2()</i> ==> <i><b>inner1()</b></i> ==> <i><b>Hello Andy</b></i>
print(ret)
帶參數裝飾器
被裝飾的函數能夠帶參數,裝飾器一樣也能夠帶參數。函數
回頭看咱們上面寫得那些裝飾器,它們默認把被裝飾的函數當成惟一的參數。可是呢,有時候咱們須要爲咱們的裝飾器傳遞參數,這種狀況下應該怎麼辦呢?post
接下來,咱們就一步步實現帶參數的裝飾器:spa
首先咱們來回顧下上面的代碼:code
def f1(func): # f1是咱們定義的裝飾器函數,func是被裝飾的函數
def f2(*arg, **kwargs): # *args和**kwargs是被裝飾函數的參數
func(*arg, **kwargs)
return f2
從上面的代碼,咱們發現了什麼?orm
個人裝飾器若是有參數的話,沒地方寫了…怎麼辦呢?string
仍是要使用閉包函數!it
咱們須要知道,函數除了能夠嵌套兩層,還能嵌套更多層:io
# 三層嵌套的函數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()