網站首頁 編程語言 正文
一、裝飾器
1.相關知識點
- *args:負責將多余的位置實參匯總,賦值給args
- **kwargs:負責將多余的關鍵字實參匯總,賦值給kwargs
命名空間與作用域
函數對象:
- 可以把函數當成參數傳入
- 可以把函數當做返回值返回
函數的嵌套定義:在函數內定義函數
閉包函數:父函數的返回值為一個函數,被返回的函數調用了父函數的局部變量,且該函數可以在父函數外部執行
裝飾器:
裝飾器:定義一個為其他函數添加功能的函數
為什么要使用裝飾器?
- 開放封閉原則:開放擴展功能但封閉源代碼的修改
- 裝飾器就是在不修改裝飾對象源代碼以及調用方式的前提下,為裝飾對象添加新功能
裝飾器實現:
需求:不修改源代碼,計算代碼執行時間
?源代碼:
import time def func0(x): ? ? time.sleep(1) ? ? print(x) func0(0) # 方案一:實現了計算代碼執行時間的功能,但修改了源代碼,不符合要求 def func1(x): ? ? start = time.time() ? ? time.sleep(1) ? ? print(x) ? ? end = time.time() ? ? print('方案一 運行時間:{}'.format(end - start)) func1(1) # 方案二:滿足要求,但代碼不具備重用性 start = time.time() func0(2) end = time.time() print('方案二 運行時間:{}'.format(end - start)) # 方案三:將方案二封裝為函數,但修改了函數的調用方式,不符合要求 def wrapper(): ? ? start = time.time() ? ? func0(3) ? ? end = time.time() ? ? print('方案三 運行時間:{}'.format(end - start)) wrapper() # 方案四:不修改原函數的調用方式 def wrapper(x): ? ? start = time.time() ? ? func0(x) ? ? end = time.time() ? ? print('方案四 運行時間:{}'.format(end - start)) wrapper(4) # 方案五:方案四參數被寫死,優化方案四 def wrapper(*args, **kwargs): ? ? start = time.time() ? ? func0(*args, **kwargs) ? ? end = time.time() ? ? print('方案五 運行時間:{}'.format(end - start)) wrapper(5) # 方案六:方案五函數被寫死,優化方案五,但修改了源代碼的調用方式 def outter(a): ? ? def wrapper(*args, **kwargs): ? ? ? ? start = time.time() ? ? ? ? a(*args, **kwargs) ? ? ? ? end = time.time() ? ? ? ? print('方案六 運行時間:{}'.format(end - start)) ? ? return wrapper f = outter(func0) f(6) # 方案七:優化方案六(outter即為裝飾器,用于裝飾func0) def outter(a): ? ? def wrapper(*args, **kwargs): ? ? ? ? start = time.time() ? ? ? ? a(*args, **kwargs) ? ? ? ? end = time.time() ? ? ? ? print('方案七 運行時間:{}'.format(end - start)) ? ? return wrapper func0 = outter(func0)?? ?# 偷梁換柱,調用者無感知 func0(7)
運行結果:
0
1
方案一 運行時間:1.001857042312622
2
方案二 運行時間:1.0040733814239502
3
方案三 運行時間:1.0017154216766357
4
方案四 運行時間:1.007995367050171
5
方案五 運行時間:1.0145602226257324
6
方案六 運行時間:1.0046615600585938
7
方案七 運行時間:1.0094060897827148
2.語法糖
- 使用語法糖,需要將裝飾器放到被裝飾對象前
# 不使用語法糖 import time def func0(x): ? ? time.sleep(1) ? ? print(x) # func0(0) def outter(a): ? ? def wrapper(*args, **kwargs): ? ? ? ? start = time.time() ? ? ? ? a(*args, **kwargs) ? ? ? ? end = time.time() ? ? ? ? print('運行時間:{}'.format(end - start)) ? ? return wrapper func0 = outter(func0) func0('hello') # 使用語法糖 import time def outter(a): ? ? def wrapper(*args, **kwargs): ? ? ? ? start = time.time() ? ? ? ? a(*args, **kwargs) ? ? ? ? end = time.time() ? ? ? ? print('運行時間:{}'.format(end - start)) ? ? return wrapper @outter # 語法糖,等價于該行func0 = outter(func0) def func0(x): ? ? time.sleep(1) ? ? print(x) func0('hello') print(func0.__name__)?
運行結果:
hello
運行時間:1.0050427913665771
wrapper
3.裝飾器模板
?# 裝飾器模板 ?def decorator_name(x): ? ? ?def wrapper(*args, **kwargs): ? ? ? ? ?# ...新添加的功能... ? ? ? ? ?# 下為調用原函數的格式 ? ? ? ? ?x(*args, **kwargs) ? ? ?return wrapper @decorator_name ?def func_name(): ? ? ?pass
擴展:真正實現偷梁換柱,調用者無感知
- 不使用裝飾器
?# 不使用裝飾器 import time def func0(x): ? ? time.sleep(1) ? ? print(x) func0('hello') print(func0.__name__) # 查看函數名? print(help(func0)) # 查看幫助信息(主要為注釋內容)
運行結果:
hello
func0
Help on function func0 in module main:func0(x)
這是函數None
使用裝飾器:
import time def outter(a): ? ? def wrapper(*args, **kwargs): ? ? ? ? '''這是裝飾器''' ? ? ? ? start = time.time() ? ? ? ? a(*args, **kwargs) ? ? ? ? end = time.time() ? ? ? ? print('運行時間:{}'.format(end - start)) ? ? return wrapper @outter # func0 = outter(func0) def func0(x): ? ? '''這是函數''' ? ? time.sleep(1) ? ? print(x) func0('hello') print(func0.__name__) print(help(func0))
運行結果:
hello
運行時間:1.011878490447998
wrapper
Help on function wrapper in module main:wrapper(*args, **kwargs)
這是裝飾器None
嘔吼,露餡了
- 解決方法,將原函數的屬性和方法,賦值給裝飾器內的wrapper
import time def outter(a): ? ? def wrapper(*args, **kwargs): ? ? ? ? '''這是裝飾器''' ? ? ? ? start = time.time() ? ? ? ? a(*args, **kwargs) ? ? ? ? end = time.time() ? ? ? ? print('運行時間:{}'.format(end - start)) ? ? wrapper.__name__ = a.__name__ ? ? wrapper.__doc__ = a.__doc__ ? ? return wrapper @outter # func0 = outter(func0) def func0(x): ? ? '''這是函數''' ? ? time.sleep(1) ? ? print(x) func0('hello') print(func0.__name__) print(help(func0))
運行結果:
hello
運行時間:1.0030155181884766
func0
Help on function func0 in module main:func0(*args, **kwargs)
這是函數None
但是,函數有很多屬性和方法,一個一個手動修改過于麻煩,甚至可能會遺漏,但python也提供了解決方法
import time from functools import wraps def outter(a): ? ? @wraps(a) ? ? def wrapper(*args, **kwargs): ? ? ? ? '''這是裝飾器''' ? ? ? ? start = time.time() ? ? ? ? a(*args, **kwargs) ? ? ? ? end = time.time() ? ? ? ? print('運行時間:{}'.format(end - start)) ? ? # wrapper.__name__ = a.__name__ ? ? # wrapper.__doc__ = a.__doc__ ? ? return wrapper @outter # func0 = outter(func0) def func0(x): ? ? '''這是函數''' ? ? time.sleep(1) ? ? print(x) func0('hello') print(func0.__name__) print(help(func0))
運行結果:
hello
運行時間:1.0114128589630127
func0
Help on function func0 in module main:func0(x)
這是函數None
4.有參裝飾器
- 裝飾器內需要傳入參數,但是由于語法糖的限制,裝飾器只能有一個參數,并且該參數僅用來接收被裝飾對象的內存地址,如何傳入其他參數?
- 解決思路:將裝飾器做成閉包函數
def outter(db_type): ? ? # 裝飾器auth ? ? def auth(x): ? ? ? ? def wrapper(*args, **kwargs): ? ? ? ? ? ? if db_type == 'file': ? ? ? ? ? ? ? ? name = input('請輸入姓名:') ? ? ? ? ? ? ? ? passwd = input('請輸入密碼:') ? ? ? ? ? ? ? ? if name == 'zhangsan' and passwd == '123': ? ? ? ? ? ? ? ? ? ? x(*args, **kwargs) ? ? ? ? ? ? ? ? ? ? print('基于文件認證') ? ? ? ? ? ? ? ? else: ? ? ? ? ? ? ? ? ? ? print('用戶名或密碼錯誤,認證失敗') ? ? ? ? ? ? elif db_type == 'mysql': ? ? ? ? ? ? ? ? x(*args, **kwargs) ? ? ? ? ? ? ? ? print('基于數據庫認證') ? ? ? ? ? ? else: ? ? ? ? ? ? ? ? print('未知認證方式,不支持') ? ? ? ? return wrapper ? ? return auth # 函數 auth = outter(db_type='file') @auth def file(): ? ? print('hello') auth = outter(db_type='mysql') @auth def mysql(): ? ? print('world') msg = input('請選擇認證方式(1=file|2=mysql):').strip() if msg =='1': ? ? file() elif msg == '2': ? ? mysql() else: ? ? print('不支持')
- 將傳入的參數寫入語法糖
def outter(db_type): ? ? # 裝飾器auth ? ? def auth(x): ? ? ? ? def wrapper(*args, **kwargs): ? ? ? ? ? ? if db_type == 'file': ? ? ? ? ? ? ? ? name = input('請輸入姓名:') ? ? ? ? ? ? ? ? passwd = input('請輸入密碼:') ? ? ? ? ? ? ? ? if name == 'zhangsan' and passwd == '123': ? ? ? ? ? ? ? ? ? ? x(*args, **kwargs) ? ? ? ? ? ? ? ? ? ? print('基于文件認證') ? ? ? ? ? ? ? ? else: ? ? ? ? ? ? ? ? ? ? print('用戶名或密碼錯誤,認證失敗') ? ? ? ? ? ? elif db_type == 'mysql': ? ? ? ? ? ? ? ? x(*args, **kwargs) ? ? ? ? ? ? ? ? print('基于數據庫認證') ? ? ? ? ? ? else: ? ? ? ? ? ? ? ? print('未知認證方式,不支持') ? ? ? ? return wrapper ? ? return auth # 函數 # auth = outter(db_type='file') # @auth @outter(db_type='file') def file(): ? ? print('hello') # auth = outter(db_type='mysql') # @auth @outter(db_type='mysql') def mysql(): ? ? print('world') msg = input('請選擇認證方式(1=file|2=mysql):').strip() if msg =='1': ? ? file() elif msg == '2': ? ? mysql() else: ? ? print('不支持')
- 模板
# 有參裝飾器模板 def out_decorator_name(x,y,z): ? ? def decorator_name(a): ? ? ? ? def wrapper(*args, **kwargs): ? ? ? ? ? ? # ...新添加的功能... ? ? ? ? ? ? # 下為調用原函數的格式 ? ? ? ? ? ? a(*args, **kwargs) ? ? ? ? return wrapper ? ? return decorator_name @out_decorator_name(x,y,z=1) def func_name(): ? ? ?pass
原文鏈接:https://blog.csdn.net/gxy_learning/article/details/123218964
相關推薦
- 2022-11-15 Django?使用VScode?創建工程的詳細步驟_python
- 2022-08-02 Python自動化測試之異常處理機制實例詳解_python
- 2022-04-11 Docker部署SonarQube的詳細流程_docker
- 2022-12-23 C++?Boost?Parameter超詳細講解_C 語言
- 2023-07-05 docker容器部署nginx外網不通端口監聽只有tcp6沒走tcp問題
- 2022-09-16 Firewalld防火墻安全防護_網絡安全
- 2022-03-26 Unity實現坦克模型_C#教程
- 2022-05-14 Python實現簡單的圖書管理系統_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支