詳解如何利用Python裝飾器優(yōu)化代碼
本文將帶你深入探討裝飾器的應(yīng)用,包括計(jì)時(shí)器裝飾器和緩存裝飾器等的實(shí)現(xiàn)。通過(guò)這些案例,我們可以看到裝飾器的強(qiáng)大和靈活,它們可以幫助我們優(yōu)化代碼,提高性能,讓我們的程序更加健壯和高效。
你是不是經(jīng)常發(fā)現(xiàn)自己寫(xiě) Python 代碼很冗余?或者已經(jīng)寫(xiě)了一些簡(jiǎn)潔的代碼,但是正常運(yùn)行時(shí)卻會(huì)遇到性能問(wèn)題?那么,Python 裝飾器就是你的救星!本文將帶你深入探討裝飾器的應(yīng)用,包括計(jì)時(shí)器裝飾器和緩存裝飾器的實(shí)現(xiàn)。
什么是裝飾器
先來(lái)了解一下什么是裝飾器。
裝飾器是 Python 的一個(gè)重要特性,它允許你將一個(gè)函數(shù)作為參數(shù)傳遞給另一個(gè)函數(shù),并返回一個(gè)新的函數(shù),而不對(duì)原始函數(shù)進(jìn)行修改。這使得你可以在不改變代碼本身的情況下,動(dòng)態(tài)地修改函數(shù)的行為。下面我們會(huì)通過(guò)具體的案例來(lái)進(jìn)一步解釋這個(gè)概念。
裝飾器的應(yīng)用
直接通過(guò)案例來(lái)理解和使用裝飾器。
計(jì)時(shí)器裝飾器
計(jì)時(shí)器裝飾器可以幫助你在代碼運(yùn)行時(shí)自動(dòng)計(jì)時(shí),以便你了解代碼的性能。下面是一個(gè)例子:
import?time #?定義計(jì)時(shí)器裝飾器 def?timer_decorator(func): ????#?定義內(nèi)部包裝函數(shù),用于接收任意數(shù)量的位置參數(shù)和關(guān)鍵字參數(shù) ????def?wrapper(*args,?**kwargs): ????????#?記錄函數(shù)運(yùn)行開(kāi)始時(shí)間 ????????start_time?=?time.time() ????????#?調(diào)用原始函數(shù)并將結(jié)果存儲(chǔ)在result中 ????????result?=?func(*args,?**kwargs) ????????#?記錄函數(shù)運(yùn)行結(jié)束時(shí)間 ????????end_time?=?time.time() ????????#?計(jì)算函數(shù)運(yùn)行時(shí)間并打印結(jié)果 ????????print(f"函數(shù)?{func.__name__}?運(yùn)行時(shí)間為?{end_time?-?start_time}?秒") ????????#?返回原始函數(shù)的結(jié)果 ????????return?result ????#?返回包裝函數(shù) ????return?wrapper @timer_decorator def?slow_function(): ????time.sleep(2) ????print("使用?timer_decorator?計(jì)算下?slow_function?函數(shù)的運(yùn)行時(shí)長(zhǎng)") slow_function()
在上面的例子中,我們定義了一個(gè)計(jì)時(shí)器裝飾器 timer_decorator
,并將它應(yīng)用到一個(gè)簡(jiǎn)單函數(shù) slow_function
上。當(dāng)我們調(diào)用 slow_function
時(shí),計(jì)時(shí)器會(huì)自動(dòng)開(kāi)始計(jì)時(shí),并在函數(shù)執(zhí)行完畢后輸出函數(shù)的運(yùn)行時(shí)間。
緩存裝飾器
緩存裝飾器可以幫助你避免重復(fù)計(jì)算,以提高代碼的性能。下面是一個(gè)例子,使用了一個(gè)緩存裝飾器來(lái)優(yōu)化遞歸斐波那契數(shù)列計(jì)算:
#?定義緩存裝飾器 def?cache_decorator(func): ????#?創(chuàng)建一個(gè)字典來(lái)存儲(chǔ)緩存的結(jié)果 ????cache?=?dict() ????#?定義內(nèi)部包裝函數(shù),用于接收任意數(shù)量的位置參數(shù) ????def?wrapper(*args): ????????#?檢查當(dāng)前參數(shù)是否在緩存中 ????????if?args?in?cache: ????????????#?如果在緩存中,則從緩存中獲取結(jié)果并打印提示信息 ????????????print(f"從緩存中獲取?{args}?的結(jié)果") ????????????return?cache[args] ????????#?如果不在緩存中,則調(diào)用原始函數(shù)計(jì)算結(jié)果 ????????result?=?func(*args) ????????#?將計(jì)算結(jié)果存儲(chǔ)到緩存中,并打印提示信息 ????????cache[args]?=?result ????????print(f"計(jì)算?{args}?的結(jié)果并將其存入緩存") ????????#?返回計(jì)算結(jié)果 ????????return?result ????#?返回包裝函數(shù) ????return?wrapper #?使用緩存裝飾器修飾fibonacci函數(shù) @cache_decorator def?fibonacci(n): ????if?n?<?2: ????????return?n ????else: ????????return?fibonacci(n-1)?+?fibonacci(n-2) print(fibonacci(3)) print("*****************") print(fibonacci(5))
在上面的例子中,我們定義了一個(gè)緩存裝飾器 cache_decorator
,并將它應(yīng)用到一個(gè)計(jì)算斐波那契數(shù)列的函數(shù) fibonacci
上。當(dāng)我們多次調(diào)用 fibonacci
函數(shù)時(shí),緩存裝飾器會(huì)自動(dòng)檢查緩存中是否已經(jīng)計(jì)算了該值,如果已經(jīng)計(jì)算則直接返回緩存中的值,否則進(jìn)行計(jì)算并將結(jié)果存入緩存。
類(lèi)型檢查裝飾器
類(lèi)型檢查裝飾器可以幫助你在函數(shù)調(diào)用時(shí)自動(dòng)檢查參數(shù)類(lèi)型,以便你避免傳入錯(cuò)誤的參數(shù)。下面是一個(gè)例子:
#?定義類(lèi)型檢查裝飾器 def?type_check_decorator(func): ????#?定義內(nèi)部包裝函數(shù),用于接收任意數(shù)量的位置參數(shù)和關(guān)鍵字參數(shù) ????def?wrapper(*args,?**kwargs): ????????#?遍歷位置參數(shù) ????????for?i,?arg?in?enumerate(args): ????????????#?如果參數(shù)不是字符串類(lèi)型,拋出TypeError異常 ????????????if?not?isinstance(arg,?str): ????????????????raise?TypeError(f"第?{i+1}?個(gè)參數(shù)值?{arg}?必須是?str?類(lèi)型") ????????#?遍歷關(guān)鍵字參數(shù) ????????for?key,?value?in?kwargs.items(): ????????????#?如果關(guān)鍵字參數(shù)的值不是字符串類(lèi)型,拋出TypeError異常 ????????????if?not?isinstance(value,?str): ????????????????raise?TypeError(f"關(guān)鍵字參數(shù)?{key}?必須是?str?類(lèi)型") ????????#?參數(shù)檢查通過(guò)后,調(diào)用原始函數(shù)并返回結(jié)果 ????????return?func(*args,?**kwargs) ????#?返回包裝函數(shù) ????return?wrapper #?使用類(lèi)型檢查裝飾器修飾concat_strings函數(shù) @type_check_decorator def?concat_strings(*strings,?sep="?"): ????return?sep.join(strings)
在上面的例子中,我們定義了一個(gè)類(lèi)型檢查裝飾器 type_check_decorator
,并將它應(yīng)用到一個(gè)將多個(gè)字符串拼接為一個(gè)字符串的函數(shù) concat_strings
上。當(dāng)我們調(diào)用 concat_strings
時(shí),類(lèi)型檢查裝飾器會(huì)自動(dòng)檢查參數(shù)類(lèi)型,并在參數(shù)類(lèi)型錯(cuò)誤時(shí)拋出異常。
日志裝飾器
日志裝飾器可以幫助你在代碼執(zhí)行時(shí)自動(dòng)記錄日志,以便你了解代碼的執(zhí)行情況。下面是一個(gè)例子:
import?logging #?定義日志裝飾器 def?log_decorator(func): ????#?配置日志記錄器,將日志記錄到文件example.log,日志級(jí)別為INFO ????logging.basicConfig(filename="example.log",?level=logging.INFO) ????#?定義內(nèi)部包裝函數(shù),用于接收任意數(shù)量的位置參數(shù)和關(guān)鍵字參數(shù) ????def?wrapper(*args,?**kwargs): ????????#?記錄函數(shù)調(diào)用信息到日志文件中 ????????logging.info(f"Calling?function?{func.__name__}") ????????#?調(diào)用原始函數(shù)并將結(jié)果存儲(chǔ)在result中 ????????result?=?func(*args,?**kwargs) ????????#?記錄函數(shù)返回值信息到日志文件中 ????????logging.info(f"Function?{func.__name__}?returned?{result}") ????????#?返回原始函數(shù)的結(jié)果 ????????return?result ????#?返回包裝函數(shù) ????return?wrapper #?使用日志裝飾器修飾add函數(shù) @log_decorator def?add(a,?b): ????return?a?+?b #?調(diào)用add函數(shù)并打印結(jié)果 print(add(1,?2))
在上面的例子中,我們定義了一個(gè)日志裝飾器 log_decorator
,并將它應(yīng)用到一個(gè)加法函數(shù) add
上。當(dāng)我們調(diào)用 add
時(shí),日志裝飾器會(huì)自動(dòng)記錄日志,并將日志信息寫(xiě)入到指定的文件中。
授權(quán)裝飾器
授權(quán)裝飾器可以幫助你在函數(shù)調(diào)用時(shí)自動(dòng)檢查用戶權(quán)限,以便你避免未授權(quán)的用戶訪問(wèn)敏感數(shù)據(jù)。下面是一個(gè)例子:
#?定義一個(gè)高階函數(shù),接受一個(gè)所需角色列表作為參數(shù) def?roles_required(required_roles): ????#?定義授權(quán)裝飾器 ????def?authorization_decorator(func): ????????#?定義包裝函數(shù),用于接收任意數(shù)量的位置參數(shù)和關(guān)鍵字參數(shù) ????????def?wrapper(*args,?**kwargs): ????????????#?獲取用戶角色,默認(rèn)為"guest" ????????????user_role?=?kwargs.get("user_role",?"guest") ????????????#?檢查用戶角色是否在所需角色列表中 ????????????if?user_role?not?in?required_roles: ????????????????#?如果不在列表中,拋出一個(gè)?PermissionError?異常 ????????????????raise?PermissionError(f"訪問(wèn)被拒絕:?需要?{',?'.join(required_roles)}?其中之一的角色") ????????????#?如果用戶角色在所需角色列表中,調(diào)用原始函數(shù)并返回結(jié)果 ????????????return?func(*args,?**kwargs) ????????#?返回包裝函數(shù) ????????return?wrapper ????#?返回授權(quán)裝飾器 ????return?authorization_decorator #?使用?roles_required?裝飾器,允許?admin?和?user?角色的用戶訪問(wèn)受保護(hù)的功能 @roles_required(["admin",?"user"]) def?protected_function(user_role="guest"): ????print("受保護(hù)的功能已成功執(zhí)行") #?嘗試使用?guest?角色訪問(wèn)受保護(hù)的功能 try: ????protected_function(user_role="guest") except?PermissionError?as?e: ????print(e) #?嘗試使用?user?角色訪問(wèn)受保護(hù)的功能 try: ????protected_function(user_role="user") except?PermissionError?as?e: ????print(e) #?嘗試使用?admin?角色訪問(wèn)受保護(hù)的功能 try: ????protected_function(user_role="admin") except?PermissionError?as?e: ????print(e)
在上面的例子中,我們首先定義了一個(gè)名為roles_required
的高階函數(shù),它接受一個(gè)required_roles
列表參數(shù)。該高階函數(shù)返回一個(gè)名為authorization_decorator
的裝飾器,該裝飾器定義了一個(gè)名為wrapper
的內(nèi)部函數(shù),用于檢查用戶角色是否在所需角色列表中。如果用戶角色在列表中,裝飾器將調(diào)用原始函數(shù)并返回結(jié)果;否則,它將拋出一個(gè)PermissionError
異常。
我們使用@roles_required(["admin", "user"])裝飾器來(lái)修飾protected_function
,確保只有具有admin或user角色的用戶可以訪問(wèn)該功能。然后,我們嘗試使用不同角色的用戶訪問(wèn)受保護(hù)的功能,以驗(yàn)證裝飾器的功能。
拓展
在上面的案例中,我們用到了兩個(gè)額外功能:高階函數(shù)和包裝器。這兩個(gè)概念在Python裝飾器的實(shí)現(xiàn)中起到了關(guān)鍵作用。接下來(lái),我們?cè)敿?xì)介紹它們的作用和應(yīng)用:
高階函數(shù)
高階函數(shù)是指接收一個(gè)或多個(gè)函數(shù)作為參數(shù)并返回一個(gè)新函數(shù)的函數(shù)。在我們的示例中,roles_required
函數(shù)是一個(gè)高階函數(shù)。它接收一個(gè)所需角色列表作為參數(shù),并返回一個(gè)名為authorization_decorator
的裝飾器函數(shù)。
高階函數(shù)在函數(shù)式編程中具有重要作用,因?yàn)樗鼈冊(cè)试S我們將函數(shù)作為參數(shù)傳遞,從而提高代碼的靈活性和復(fù)用性。
高階函數(shù)的應(yīng)用:
- 實(shí)現(xiàn)代碼復(fù)用和模塊化
- 簡(jiǎn)化代碼結(jié)構(gòu),提高代碼可讀性
- 實(shí)現(xiàn)函數(shù)式編程范式,支持函數(shù)作為參數(shù)傳遞和返回值
包裝器
包裝器(Wrapper)是一個(gè)用于修改其他函數(shù)行為的函數(shù)。在我們的示例中,wrapper函數(shù)是一個(gè)包裝器。它接收任意數(shù)量的位置參數(shù)和關(guān)鍵字參數(shù),然后根據(jù)特定邏輯(在這個(gè)例子中是檢查用戶角色)來(lái)決定是否調(diào)用原始函數(shù)(即被裝飾的函數(shù))。
包裝器的應(yīng)用:
- 修改或增強(qiáng)原始函數(shù)的行為,而無(wú)需修改原始函數(shù)的代碼
- 實(shí)現(xiàn)代碼的解耦,將不同功能分開(kāi)
- 提高代碼的可讀性和可維護(hù)性
在裝飾器中,高階函數(shù)和包裝器共同發(fā)揮作用。高階函數(shù)負(fù)責(zé)接收參數(shù)并生成裝飾器,而裝飾器內(nèi)部的包裝器負(fù)責(zé)根據(jù)特定邏輯對(duì)原始函數(shù)進(jìn)行修改或增強(qiáng)。這種組合提供了一種靈活且強(qiáng)大的方式來(lái)擴(kuò)展和修改函數(shù)的行為,同時(shí)保持代碼的模塊化和易于維護(hù)。
總結(jié)
本文介紹了 Python 裝飾器的基本概念和幾種常見(jiàn)的應(yīng)用場(chǎng)景:計(jì)時(shí)器裝飾器和緩存裝飾器等。通過(guò)這些案例,我們可以看到裝飾器的強(qiáng)大和靈活,它們可以幫助我們優(yōu)化代碼,提高性能,讓我們的程序更加健壯和高效。
到此這篇關(guān)于詳解如何利用Python裝飾器優(yōu)化代碼的文章就介紹到這了,更多相關(guān)Python裝飾器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python 用opencv調(diào)用訓(xùn)練好的模型進(jìn)行識(shí)別的方法
今天小編就為大家分享一篇python 用opencv調(diào)用訓(xùn)練好的模型進(jìn)行識(shí)別的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-12-12關(guān)于Python如何避免循環(huán)導(dǎo)入問(wèn)題詳解
在大型的Python工程中,由于架構(gòu)設(shè)計(jì)不當(dāng),可能會(huì)出現(xiàn)模塊間相互引用的情況。下面這篇文章主要給大家介紹了關(guān)于如何避免Python的循環(huán)導(dǎo)入問(wèn)題的相關(guān)資料,需要的朋友可以參考借鑒,下面來(lái)一起看看吧。2017-09-09Pygame庫(kù)200行代碼實(shí)現(xiàn)簡(jiǎn)易飛機(jī)大戰(zhàn)
本文主要介紹了Pygame庫(kù)200行代碼實(shí)現(xiàn)簡(jiǎn)易飛機(jī)大戰(zhàn),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-12-12Python中的TfidfVectorizer參數(shù)使用解析
這篇文章主要介紹了Python中的TfidfVectorizer參數(shù)使用解析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11python學(xué)習(xí)筆記之調(diào)用eval函數(shù)出現(xiàn)invalid syntax錯(cuò)誤問(wèn)題
python是一門(mén)多種用途的編程語(yǔ)言,時(shí)常扮演腳本語(yǔ)言的角色。一般來(lái)說(shuō),python可以定義為面向?qū)ο蟮哪_本語(yǔ)言,這個(gè)定義把面向?qū)ο蟮闹С趾兔嫦蚰_本語(yǔ)言的角色融合在一起。很多時(shí)候,人們常常喜歡用“腳本”和不是語(yǔ)言來(lái)描述python的代碼文件。2015-10-10Python如何使用標(biāo)準(zhǔn)庫(kù)tmpfile庫(kù)創(chuàng)建臨時(shí)文件
這篇文章主要介紹了Python如何使用標(biāo)準(zhǔn)庫(kù)tmpfile庫(kù)創(chuàng)建臨時(shí)文件問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-02-02Python利用多線程同步鎖實(shí)現(xiàn)多窗口訂票系統(tǒng)(推薦)
這篇文章主要介紹了Python利用多線程同步鎖實(shí)現(xiàn)多窗口訂票系統(tǒng),主要是利用threading.lock()通過(guò)實(shí)例代碼相結(jié)合給大家講解的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-12-12從訓(xùn)練好的tensorflow模型中打印訓(xùn)練變量實(shí)例
今天小編就為大家分享一篇從訓(xùn)練好的tensorflow模型中打印訓(xùn)練變量實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-01-01