裝飾的工具python
必需要遵循的原則:「開放封閉」閉包
開放:對源函數功能的添加是開放的app
封閉:對源函數功能修改是封閉的函數
在不修改被裝飾對象源代碼和調用方式的前提下,增長新功能工具
必須遵循的原則:url
一、不修改被裝飾對象的源代碼code
二、不修改被裝飾對象的調用方式對象
解決代碼冗餘問題,提升代碼可擴展性blog
編寫裝飾器經過閉包函數實現ip
裝飾器推導過程
# # 裝飾器推導過程 def move(): '''下載電影的源函數''' print('開始下載') time.sleep(2) print('下載完成') def fun(index): def down(): star_time = time.time() index() # 將被裝飾對象move賦值給index執行 end_time = time.time() print(f'總耗時{end_time - star_time}') return down fun(move)()
# 有返回值時 def move(): '''下載電影的源函數''' print('開始下載') time.sleep(2) print('下載完成') return '明日之巔.mp4' def fun(index): def down(): star_time = time.time() res = index() # 將index執行結果賦值給res # print(res) end_time = time.time() print(f'總耗時{end_time - star_time}') return res # 將index返回值返回出來 # print(down()) return down move = fun(move) # 將調用方式改爲被裝飾對象 move()
# 源函數有參數時 def move(url): '''下載電影的源函數''' print(f'{url}開始下載') time.sleep(2) print('下載完成') return '明日之巔.mp4' def wrapper(func): def inner(*args, **kwargs): start_time = time.time() res = func(*args, **kwargs) end_time = time.time() print(f'總耗時:{end_time - start_time}') return res return inner move = wrapper(move) move(('https://www.cnblogs.com'))
裝飾器最終模板
def wrapper(func): def inner(*args, **kwargs): # 調用前增長新功能 res = func(*args, **kwargs) # 調用被裝飾對象,接收返回值 # 調用後增長的新功能 return res # 接收被裝飾對象的返回值 return inner
一、裝飾器的語法糖是屬於裝飾器的
二、用@+裝飾器名,在被裝飾對象開頭
三、在使用裝飾器語法糖時,裝飾器必須定義在被裝飾對象之上
# 增長統計代碼運行時間的裝飾器 def wrapper(func): def inner(*args,**kwargs): start_time = time.time() res = func(*args,**kwargs) end_time = time.time() print(end_time-start_time) return res return inner # 使用語法糖 @wrapper def movie(): print("開始下載") time.sleep(2) print('下載結束') # 不使用語法糖調用增長新功能後的函數 # movie = wrapper(movie) # movie() # 使用語法糖後可直接調用 movie()
在同一個被裝飾對象中,添加多個裝飾器,並執行
每個新功能都寫一個新的裝飾器,不然會致使代碼冗餘,結構不清晰,可擴展性差
@裝飾器1 @裝飾器2 @裝飾器3 def 被裝飾對象(): pass
注意:在調用被裝飾對象時,纔會執行添加的新功能
疊加裝飾器裝飾順序,執行順序:
裝飾的順序:由下往上裝飾
執行的順序:由上往下執行
注意:
一、裝飾器內函數inner中出現任何判斷,最後都要返回「調用後的被裝飾對象」func(*args,**kwargs)
二、被裝飾對象在調用時,若是還有其餘裝飾器,會先執行其餘裝飾器中的inner
def wrapper1(func1): def inner1(*args, **kwargs): print("1————start") res = func1(*args, **kwargs) print("1————end") return res return inner1 def wrapper2(func2): def inner2(*args, **kwargs): print("2————start") res = func2(*args, **kwargs) print("2————end") return res return inner2 def wrapper3(func3): def inner3(*args, **kwargs): print("3————start") res = func3(*args, **kwargs) print("3————end") return res return inner3 @wrapper1 @wrapper2 @wrapper3 def index(): print("index") index() >>> # 執行順序:由上而下 # 裝飾順序:由下而上 1————start 2————start 3————start index 3————end 2————end 1————end
無參裝飾器:裝飾在被裝飾對象時,沒有傳參數的裝飾器
# 如下是無參裝飾器 @wrapper1 @wrapper2 @wrapper3
有參裝飾器:在某些時候咱們須要給用戶的權限進行分類
# 如下是有參裝飾器 @wrapper1(參數1) @wrapper2(參數2) @wrapper3(參數3)
若是咱們想提供多種不一樣的認證方式以供選擇 , 函數inner須要一個driver參數,而函數wrapper與inner的參數都有其特定的功能,不能用來接受其餘類別的參數,能夠在wrapper的外部再包一層函數user_auth,用來專門接受額外的參數,這樣便保證了在user_auth函數內不管多少層均可以引用到
# 給用戶的權限進行分類 def user_auth(driver): def wrapper(func): def inner(*args, **kwargs): if driver == "svip": # 加入超級會員的功能 res = func(*args, **kwargs) return res elif driver == "vip": # 加入會員的功能 res = func(*args, **kwargs) return res else: # 加入普通用戶的功能 res = func(*args, **kwargs) return res return inner return wrapper @user_auth(driver='svip') def user_power(): pass user_power()
是一個修復工具,修復的是被裝飾對象的空間
一、先從functools中調用wraps >>> from functools import wraps
二、裝飾器在定義內層函數前加@wraps(func) 修更名稱空間:將inner >>> func
三、inner前加@wraps(func)是將inner名稱空間修改到func,若是不用@wraps(func)打印註釋本質打印的是inner的註釋,加上@wraps(func)修復被裝飾對象的空間,打印註釋就會打印被裝飾對象的註釋
# 函數對象.__doc__ 是查看內部的註釋 from functools import wraps # 從functools中調用wraps模塊 def wrapper(func): @wraps(func) # 修更名稱空間,inner >>> func def inner(*args,**kwargs): ''' 裝飾器的註釋 ''' # 調用被裝飾對象前加的功能 res = func(*args,**kwargs) # 調用被裝飾對象前加的功能 return res return inner @wrapper def index(): ''' 被裝飾對象index的註釋 :return: ''' pass print(index.__doc__) # 加上@wraps(func) >>> 被裝飾對象index的註釋 :return: # 不加@wraps(func) >>> 裝飾器的註釋