器指的是工具,而程序中的函數就是具有某一功能的工具,因此裝飾器指的是爲被裝飾器對象添加額外功能。所以定義裝飾器就是定義一個函數,只不過該函數的功能是用來爲其餘函數添加額外的功能。mysql
須要注意的是:sql
若是咱們已經上線了一個項目,咱們須要修改某一個方法,可是咱們不想修改方法的使用方法,這個時候可使用裝飾器。由於軟件的維護應該遵循開放封閉原則,即軟件一旦上線運行後,軟件的維護對修改源代碼是封閉的,對擴展功能指的是開放的。mongodb
裝飾器的實現必須遵循兩大原則:閉包
裝飾器其實就是在遵循以上兩個原則的前提下爲被裝飾對象添加新功能。app
改變源代碼函數
import time def index(): start = time.time() print('welcome to index') time.sleep(1) end = time.time() print(F"index run time is {start-end}") index() welcome to index index run time is -1.0008180141448975
編寫重複代碼工具
import time def index(): print('welcome to index') time.sleep(1) def f2(): print('welcome to index') time.sleep(1) start = time.time() index() end = time.time() print(F"index run time is {start-end}") start = time.time() f2() end = time.time() print(F"f2 run time is {start-end}") welcome to index index run time is -1.0046868324279785 welcome to index f2 run time is -1.000690221786499
第一種傳參方式:改變調用方式3d
import time def index(): print('welcome to index') time.sleep(1) def time_count(func): start = time.time() func() end = time.time() print(f"{func} time is {start-end}") time_count(index) welcome to index <function index at 0x102977378> time is -1.000748872756958
第二種傳參方式:包給函數-外包code
import time def index(): print('welcome to index') time.sleep(1) def time_count(func): # func = 最原始的index def wrapper(): start = time.time() func() end = time.time() print(f"{func} time is {start-end}") return wrapper # f = time_count(index) # f() index = time_count(index) # index爲被裝飾函數的內存地址,即index = wrapper index() # wrapper() welcome to index <function index at 0x102977730> time is -1.0038220882415771
上述的裝飾器,最後調用index()的時候,實際上是在調用wrapper(),所以若是原始的index()有返回值的時候,wrapper()函數的返回值應該和index()的返回值相同,也就是說,咱們須要同步原始的index()和wrapper()方法的返回值。對象
import time def index(): print('welcome to index') time.sleep(1) return 123 def time_count(func): # func = 最原始的index def wrapper(): start = time.time() res = func() end = time.time() print(f"{func} time is {start-end}") return res return wrapper index = time_count(index) res = index() print(f"res: {res}") welcome to index <function index at 0x102977620> time is -1.0050289630889893 res: 123
若是原始的index()方法須要傳參,那麼咱們以前的裝飾器是沒法實現該功能的,因爲有wrapper()=index(),因此給wrapper()方法傳參便可。
import time def index(): print('welcome to index') time.sleep(1) return 123 def home(name): print(f"welcome {name} to home page") time.sleep(1) return name def time_count(func): # func = 最原始的index def wrapper(*args, **kwargs): start = time.time() res = func(*args, **kwargs) end = time.time() print(f"{func} time is {start-end}") return res return wrapper home = time_count(home) res = home('egon') print(f"res: {res}") welcome egon to home page <function home at 0x102977378> time is -1.0039079189300537 res: egon
在被裝飾函數正上方,而且是單獨一行寫上@裝飾器名
import time def time_count(func): # func = 最原始的index def wrapper(*args, **kwargs): start = time.time() res = func(*args, **kwargs) end = time.time() print(f"{func} time is {start-end}") return res return wrapper @time_count # home = time_count(home) def home(name): print(f"welcome {name} to home page") time.sleep(1) return name @time_count # index = time_count(index) def index(): print('welcome to index') time.sleep(1) return 123 res = home('egon') print(f"res: {res}") welcome egon to home page <function home at 0x102977620> time is -1.0005171298980713 res: egon
def deco(func): def wrapper(*args,**kwargs): res = func(*args,**kwargs) return res return wrapper
無參裝飾器只套了兩層,本節將講一個套三層的裝飾器——有參裝飾器,但如今咱們先實現一個用戶登陸註冊的裝飾器。
import time current_user = {'username': None} def login(func): # func = 最原始的index def wrapper(*args, **kwargs): if current_user['username']: res = func(*args, **kwargs) return res user = input('username: ').strip() pwd = input('password: ').strip() if user == 'nick' and pwd == '123': print('login successful') current_uesr['usre'] = user res = func(*args, **kwargs) return res else: print('user or password error') return wrapper @login def home(name): print(f"welcome {name} to home page") time.sleep(1) return name @login def index(): print('welcome to index') time.sleep(1) return 123 res = index() username: nick password: 123 login successful welcome to index
對於上面的登陸註冊,咱們把用戶登陸成功的信息寫入內存當中。可是在工業上,用戶信息能夠存在文本中、mysql中、mongodb當中,可是咱們只讓用戶信息來自於file
的用戶能夠認證。所以咱們能夠改寫上述的裝飾器。
import time current_user = {'username': None} def login(func): # func = 最原始的index def wrapper(*args, **kwargs): if current_user['username']: res = func(*args, **kwargs) return res user = input('username: ').strip() pwd = input('password: ').strip() engine = 'file' if engine == 'file': print('base of file') if user == 'nick' and pwd == '123': print('login successful') current_uesr['usre'] = user res = func(*args, **kwargs) return res else: print('user or password error') elif engine == 'mysql': print('base of mysql') elif engine == 'mongodb': print('base of mongodb') else: print('default') return wrapper @login def home(name): print(f"welcome {name} to home page") time.sleep(1) @login def index(): print('welcome to index') time.sleep(1) res = index() username: nick password: 123 base of file login successful welcome to index
咱們首先看看閉包,包三層怎麼運用。
def f1(y): def f2(): x = 1 def f3(): print(f"x: {x}") print(f"y: {y}") return f3 return f2 f2 = f1(2) f3 = f2() f3() x: 1 y: 2
如今需求改了,咱們須要判斷用戶動態的獲取用戶密碼的方式,若是是file
類型的,咱們則讓用戶進行認證。所以咱們可使用有參裝飾器。
import time current_uesr = {'username': None} def auth(engine='file'): def login(func): # func = 最原始的index def wrapper(*args, **kwargs): if current_user['username']: res = func(*args, **kwargs) return res user = input('username: ').strip() pwd = input('password: ').strip() if engine == 'file': print('base of file') if user == 'nick' and pwd == '123': print('login successful') current_uesr['usre'] = user res = func(*args, **kwargs) return res else: print('user or password error') elif engine == 'mysql': print('base of mysql, please base of file') elif engine == 'mongodb': print('base of mongodb, please base of file') else: print('please base of file') return wrapper return login @auth(engine='mysql') def home(name): print(f"welcome {name} to home page") time.sleep(1) @auth(engine='file') def index(): print('welcome to index') time.sleep(1) res = index() username: nick password: 123 base of file login successful welcome to index
因爲兩層的裝飾器,參數必須得固定位func
,可是三層的裝飾器解除了這個限制。咱們不單單可使用上述單個參數的三層裝飾器,多個參數的只須要在三層裝飾器中多加入幾個參數便可。也就是說裝飾器三層便可,多加一層反倒無用。