裝飾器的一個特色是:在執行原函數的基礎上,增長內容。
我們舉個簡單的例子:
咱們能夠把它用做日誌打印器,在執行函數前,會有禮貌的告知你一下
「親愛的coder,我要執行了」
在執行結束後,也會告知各位coder。
是否是頗有趣?
那麼,我們如何執行原函數那?
✔ 把原函數的名字交給裝飾函數,裝飾函數就具有了執行老函數的能力。python
舊函數,待裝飾的函數app
def fun(): print('我是老函數') def factory(old_fun): print('='*20) old_fun() print('='*20) factory(fun)
❓ 上面的裝飾行爲還有哪些問題?
◇ 須要經過裝飾工廠來調用原函數,原來調用這個功能的業務部門,將大面積修改代碼;
❓ 那麼如何改進這個問題那?
如何設計一個方式,在不改變原函數名稱的狀況下,調用時也執行裝飾工廠那。
◇ old_name = factory(old_name)ide
def fun(): print("我是fun") def factory(a): def tmp(): print("="*10) a() print("="*10) return tmp fun = factory(fun) fun()
old_name = factory(old_name)這句話就是生成裝飾器的核心語句。爲了不每次都寫這樣的無腦代碼,python提供了一個便捷的寫法,咱們稱之爲語法糖。
語法糖的寫法:
· 找到要裝飾的函數
· 在上面寫上@,而後跟裝飾器函數的名稱br/>@decortate
def fun():
print('in fun() ...')
1.1.4.總結
python的裝飾器寫法思路:
1.定義一箇中介函數,該函數命名爲裝飾行爲,接收一個可調用對象
def get_timer(fn):
pass
2.在中介函數內部實現裝飾行爲函數
def get_timer(fn):
def wrapper():
... ... # 裝飾代碼實現
3.在中介函數最後返回裝飾行爲的函數名:
def get_timer(fn):
def wrapper():
... ... # 裝飾代碼實現
return wrapperbr/>【注意】:返回的是函數名,而不是函數調用
4.在須要被裝飾的函數前,定義@裝飾器名稱
@get_timer
def fun():
pass函數
1.2.1.無參數裝飾器
Demo1: 增長函數何時運行的功能:設計
import time def time_fun(func): def my_time(): print(f"{func.__name__} running at {time.ctime()}") func() return my_time @time_fun def fun1(): print("+++++++++") fun1() time.sleep(2) fun1()
裝飾器的核心是內部函數的行爲被重命名;
內部函數的形式要知足原函數形式;日誌
import time def time_fun(func): def my_time(arg1): # 內部函數裏預留接口 print(f"{func.__name__} running at {time.ctime()}") func(arg1) # 原函數傳遞參數 return my_time @time_fun def fun1(arg1): print("+++++{}++++".format(arg1)) fun1(10) time.sleep(2) fun1(10)
原函數有返回值,內部裝飾函數也接收返回值進行返回。
import timecode
def time_fun(func): def my_time(*args, **kwargs): print(f"{func.__name__} running at {time.ctime()}") func(*args, **kwargs) return my_time @time_fun def fun1(arg1, arg2, name): print("+++++{},{},{}++++".format(arg1, arg2, name)) fun1(10, 20, name='rocky') time.sleep(2) fun1(10, 20, name='jim')
在裝飾器中,咱們也有一種需求,根據傳入的參數不一樣,裝飾的行爲方式也會不一樣,那麼如何定義這種裝飾器那。
首先再理解裝飾器的語法:orm
@time_fun def fun1(arg1, arg2, name): print("+++++{},{},{}++++".format(arg1, arg2, name)) 原材料名稱= 裝飾器名稱(原材料名稱) @time_fun(「itsource」) def fun1(arg1, arg2, name): print("+++++{},{},{}++++".format(arg1, arg2, name)) fun1 = time_fun(「itsource」)(fun1)
這樣來看,必需要保證time_fun(「itsource」)返回的內容剛好是標準的裝飾器就能夠知足了。
import time對象
def time_fun(flags): def time_arg(func): def my_time(*args, **kwargs): print(f"{func.__name__} running at {time.ctime()}") print("the flags is {}".format(flags)) return func(*args, **kwargs) return my_time return time_arg @time_fun("itsource") def fun1(arg1, arg2, name): print("+++++{},{},{}++++".format(arg1, arg2, name)) fun1(10, 20, name='rocky') time.sleep(2) fun1(10, 20, name='jim')
❓ 可執行對象到底是什麼?
✔ 具備call方法的對象空間,就稱之爲可執行對象,每定義一個函數,就至關於在空間中定義了call方法。
❓ 如何利用類來實現裝飾器
@Test # fun = Test(fun)
def fun():
pass
Test的init方法應該接收被裝飾函數的名稱
調用fun()實際就是調用了Test類的call方法
class Test():
def init(self, fn):
self.old_fun = fn接口
def __call__(self,): ... ... # 裝飾內容 ret = self.old_fun()
@Test
def fun(a, b, name):
pass
fun()
在實際開發時,一個原函數,可使用多個裝飾函數進行修飾,這種行爲就稱之爲多層裝飾。
對於多層裝飾,要理解他的執行過程。
def bold(fn): def wrapper(): return f'<b>{fn()}</b>' return wrapper def italic(fn): def wrapper(): return f'<i>{fn()}</i>' return wrapper @italic @bold def hello(): return 'Hello World' print(hello())
【結果】:<i><b>Hello World</b></i>1.4.總結裝飾器實際也是一個函數(可調用對象);接收一個老函數的名稱;返回新函數的名稱;在新函數裏調用老函數;