Python裝飾器有哪些絕妙的用法
裝飾器的價值不言而喻,可以用來增強函數(shù)功能、簡化代碼、減少代碼冗余。
它的使用場景同樣很多,比較簡單的場景包含打印日志、統(tǒng)計運行時間,這類例子和用法網(wǎng)上已經(jīng)很多了:
def time_dec(func): ? def wrapper(*arg): t = time.clock() res = func(*arg) print func.func_name, time.clock()-t return res ? return wrapper ? ? @time_dec def myFunction(n): ...
再進階一些的,可以用來校驗函數(shù)傳入?yún)?shù)類型、線程同步、單元測試等:
@parameters( (2, 4, 6), (5, 6, 11), ) def test_add(a, b, expected): assert a + b == expected
目前可以用的裝飾器可以分為如下幾類:
- 自定義
- 第三方工具包
- 內(nèi)置
下面就分別來介紹一下。
自定義
關(guān)于自定義的裝飾器在前面已經(jīng)提到了,我在開發(fā)過程中經(jīng)常用到的就是日志打印、計時、數(shù)據(jù)校驗等場景,通過裝飾器可以提高代碼的簡潔性,避免重復造輪子。
除了這些基本的,也有一些比較實用的地方。
作為開發(fā)同學,肯定會遇到不同的運行環(huán)境:
- 開發(fā)環(huán)境
- 測試環(huán)境
- 生產(chǎn)環(huán)境
有時候,我們期望一個函數(shù)在不同環(huán)境下執(zhí)行不同的過程,產(chǎn)出不同的結(jié)果,做一些環(huán)境的隔離和差異化處理。
通過裝飾器就可以很好的解決:
production_servers = [...] ? def production(func: Callable): def inner(*args, **kwargs): if gethostname() in production_servers: return func(*args, **kwargs) else: print('This host is not a production server, skipping function decorated with @production...') return inner ? def development(func: Callable): def inner(*args, **kwargs): if gethostname() not in production_servers: return func(*args, **kwargs) else: print('This host is a production server, skipping function decorated with @development...') return inner ? def sit(func: Callable): def inner(*args, **kwargs): print('Skipping function decorated with @sit...') return inner ? @production def foo(): print('Running in production, touching databases!') ? foo() ? @development def foo(): print('Running in production, touching databases!') ? foo() ? @inactive def foo(): print('Running in production, touching databases!') ? foo()
簡單的介紹一下這段代碼。
在這里,先是羅列了生產(chǎn)環(huán)境的服務(wù)列表,然后分別定義了生產(chǎn)、開發(fā)、測試環(huán)境的裝飾器,然后給同名的函數(shù)就可以配上對應(yīng)的裝飾器。
在執(zhí)行代碼的過程中,這段代碼會首先獲取hostname,自動判斷所在環(huán)境,然后執(zhí)行對應(yīng)函數(shù)。
第三方工具包
上面是根據(jù)我們在開發(fā)過程中遇到的個性化場景進行來自定義一個裝飾器。
作為一款以工具包著稱的編程語言,Python中也有很多工具包提供了一些實用的裝飾器。
以日志為例,這是每個程序員都無法繞開的。
調(diào)試程序?qū)τ诖蠖鄶?shù)開發(fā)者來說是一項必不可少的工作,當我們想要知道代碼是否按照預期的效果在執(zhí)行時,我們會想到去輸出一下局部變量與預期的進行比對。目前大多數(shù)采用的方法主要有以下幾種:
- Print函數(shù)
- Log日志
- IDE調(diào)試器
但是這些方法有著無法忽視的弱點:
- 繁瑣
- 過度依賴工具
其中有一款不錯的開源工具PySnooper就通過裝飾器把這個問題巧妙的解決了。
PySnooper的調(diào)用方式就是通過@pysnooper.snoop的方式進行使用,該裝飾器可以傳入一些參數(shù)來實現(xiàn)一些目的,具體如下:
參數(shù)描述:
- None輸出日志到控制臺
- filePath輸出到日志文件,例如'log/file.log'
- prefix給調(diào)試的行加前綴,便于識別
- watch查看一些非局部變量表達式的值
- watch_explode展開值用以查看列表/字典的所有屬性或項
- depth顯示函數(shù)調(diào)用的函數(shù)的snoop行
舉個例子:
import numpy as np import pysnooper ? @pysnooper.snoop() def one(number): mat = [] while number: mat.append(np.random.normal(0, 1)) number -= 1 return mat ? one(3)
然后,就會給出如下輸出:
Starting var:.. number = 3
22:17:10.634566 call 6 def one(number):
22:17:10.634566 line 7 mat = []
New var:....... mat = []
22:17:10.634566 line 8 while number:
22:17:10.634566 line 9 mat.append(np.random.normal(0, 1))
Modified var:.. mat = [-0.4142847169210746]
22:17:10.634566 line 10 number -= 1
Modified var:.. number = 2
22:17:10.634566 line 8 while number:
22:17:10.634566 line 9 mat.append(np.random.normal(0, 1))
Modified var:.. mat = [-0.4142847169210746, -0.479901983375219]
22:17:10.634566 line 10 number -= 1
Modified var:.. number = 1
22:17:10.634566 line 8 while number:
22:17:10.634566 line 9 mat.append(np.random.normal(0, 1))
Modified var:.. mat = [-0.4142847169210746, -0.479901983375219, 1.0491540468063252]
22:17:10.634566 line 10 number -= 1
Modified var:.. number = 0
22:17:10.634566 line 8 while number:
22:17:10.634566 line 11 return mat
22:17:10.634566 return 11 return mat
Return value:.. [-0.4142847169210746, -0.479901983375219, 1.0491540468063252]
局部變量值、代碼片段、局部變量所在行號、返回結(jié)果等,這些關(guān)鍵信息都輸出了,既方便,又清晰。
內(nèi)置
除了自定義和第三方工具包之外,Python還內(nèi)置了很多不錯的裝飾器,例如@abc.abstractmethod、@asyncio.coroutine、@classmethod等等。
這里著重提一個非常強大的裝飾器,能夠極大的提升Python的運行速度和效率,通過一個裝飾器能夠?qū)ython代碼的執(zhí)行速度提升上萬倍,這個裝飾器就是@functools.lru_cache。
以比較知名的斐波那契數(shù)列的例子來演示一下。
由于它遞歸計算的過程中,還會用到之前計算的結(jié)果,因此會涉及較多的重復計算,下面先看一下正常計算的耗時情況。
import time as tt ? def fib(n): if n <= 1: return n return fib(n-1) + fib(n-2) ? t1 = tt.time() fib(30) print("Time taken: {}".format(tt.time() - t1)) # 0.2073
n等于30時,耗時0.2073。
加上@functools.lru_cache裝飾器再看一下:
import time as tt import functools ? @functools.lru_cache(maxsize=5) def fib(n): if n <= 1: return n return fib(n-1) + fib(n-2) ? t1 = tt.time() fib(30) print("Time taken: {}".format(tt.time() - t1)) # 1.811981e-05
耗時為1.811981e-05,足足差了4個量級,快了10000+倍!
到此這篇關(guān)于Python裝飾器有哪些絕妙的用法的文章就介紹到這了,更多相關(guān)Python裝飾器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
mac系統(tǒng)下安裝pycharm、永久激活、中文漢化詳細教程
這篇文章主要介紹了mac系統(tǒng)下安裝pycharm、永久激活、中文漢化詳細教程,需要的朋友可以參考下2020-11-11python3.6.3轉(zhuǎn)化為win-exe文件發(fā)布的方法
今天小編就為大家分享一篇python3.6.3轉(zhuǎn)化為win-exe文件發(fā)布的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-10-10基于Python實現(xiàn)視頻分辨率轉(zhuǎn)換
這篇文章主要介紹了基于Python實現(xiàn)視頻的分辨率轉(zhuǎn)換的示例代碼,文中的代碼講解詳細,對學習Python有一定的幫助,感興趣的小伙伴可以了解一下2021-12-12Python正則表達式函數(shù)match()和search()使用全面指南
在Python中,正則表達式是強大的工具,能夠用于文本匹配、搜索和替換,re模塊提供了許多函數(shù)來處理正則表達式,其中match()和search()是兩個常用的函數(shù),本文將深入探討這兩個函數(shù)的用法、區(qū)別和示例,幫助你更好地理解它們的功能2024-01-01