Python裝飾器有哪些絕妙的用法
裝飾器的價(jià)值不言而喻,可以用來(lái)增強(qiáng)函數(shù)功能、簡(jiǎn)化代碼、減少代碼冗余。
它的使用場(chǎng)景同樣很多,比較簡(jiǎn)單的場(chǎng)景包含打印日志、統(tǒng)計(jì)運(yùn)行時(shí)間,這類(lèi)例子和用法網(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):
...
再進(jìn)階一些的,可以用來(lái)校驗(yàn)函數(shù)傳入?yún)?shù)類(lèi)型、線(xiàn)程同步、單元測(cè)試等:
@parameters(
(2, 4, 6),
(5, 6, 11),
)
def test_add(a, b, expected):
assert a + b == expected
目前可以用的裝飾器可以分為如下幾類(lèi):
- 自定義
- 第三方工具包
- 內(nèi)置
下面就分別來(lái)介紹一下。
自定義
關(guān)于自定義的裝飾器在前面已經(jīng)提到了,我在開(kāi)發(fā)過(guò)程中經(jīng)常用到的就是日志打印、計(jì)時(shí)、數(shù)據(jù)校驗(yàn)等場(chǎng)景,通過(guò)裝飾器可以提高代碼的簡(jiǎn)潔性,避免重復(fù)造輪子。
除了這些基本的,也有一些比較實(shí)用的地方。
作為開(kāi)發(fā)同學(xué),肯定會(huì)遇到不同的運(yùn)行環(huán)境:
- 開(kāi)發(fā)環(huán)境
- 測(cè)試環(huán)境
- 生產(chǎn)環(huán)境
有時(shí)候,我們期望一個(gè)函數(shù)在不同環(huán)境下執(zhí)行不同的過(guò)程,產(chǎn)出不同的結(jié)果,做一些環(huán)境的隔離和差異化處理。
通過(guò)裝飾器就可以很好的解決:
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()
簡(jiǎn)單的介紹一下這段代碼。
在這里,先是羅列了生產(chǎn)環(huán)境的服務(wù)列表,然后分別定義了生產(chǎn)、開(kāi)發(fā)、測(cè)試環(huán)境的裝飾器,然后給同名的函數(shù)就可以配上對(duì)應(yīng)的裝飾器。
在執(zhí)行代碼的過(guò)程中,這段代碼會(huì)首先獲取hostname,自動(dòng)判斷所在環(huán)境,然后執(zhí)行對(duì)應(yīng)函數(shù)。
第三方工具包
上面是根據(jù)我們?cè)陂_(kāi)發(fā)過(guò)程中遇到的個(gè)性化場(chǎng)景進(jìn)行來(lái)自定義一個(gè)裝飾器。
作為一款以工具包著稱(chēng)的編程語(yǔ)言,Python中也有很多工具包提供了一些實(shí)用的裝飾器。
以日志為例,這是每個(gè)程序員都無(wú)法繞開(kāi)的。
調(diào)試程序?qū)τ诖蠖鄶?shù)開(kāi)發(fā)者來(lái)說(shuō)是一項(xiàng)必不可少的工作,當(dāng)我們想要知道代碼是否按照預(yù)期的效果在執(zhí)行時(shí),我們會(huì)想到去輸出一下局部變量與預(yù)期的進(jìn)行比對(duì)。目前大多數(shù)采用的方法主要有以下幾種:
- Print函數(shù)
- Log日志
- IDE調(diào)試器
但是這些方法有著無(wú)法忽視的弱點(diǎn):
- 繁瑣
- 過(guò)度依賴(lài)工具
其中有一款不錯(cuò)的開(kāi)源工具PySnooper就通過(guò)裝飾器把這個(gè)問(wèn)題巧妙的解決了。
PySnooper的調(diào)用方式就是通過(guò)@pysnooper.snoop的方式進(jìn)行使用,該裝飾器可以傳入一些參數(shù)來(lái)實(shí)現(xiàn)一些目的,具體如下:
參數(shù)描述:
- None輸出日志到控制臺(tái)
- filePath輸出到日志文件,例如'log/file.log'
- prefix給調(diào)試的行加前綴,便于識(shí)別
- watch查看一些非局部變量表達(dá)式的值
- watch_explode展開(kāi)值用以查看列表/字典的所有屬性或項(xiàng)
- depth顯示函數(shù)調(diào)用的函數(shù)的snoop行
舉個(gè)例子:
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)
然后,就會(huì)給出如下輸出:
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]
局部變量值、代碼片段、局部變量所在行號(hào)、返回結(jié)果等,這些關(guān)鍵信息都輸出了,既方便,又清晰。
內(nèi)置
除了自定義和第三方工具包之外,Python還內(nèi)置了很多不錯(cuò)的裝飾器,例如@abc.abstractmethod、@asyncio.coroutine、@classmethod等等。
這里著重提一個(gè)非常強(qiáng)大的裝飾器,能夠極大的提升Python的運(yùn)行速度和效率,通過(guò)一個(gè)裝飾器能夠?qū)ython代碼的執(zhí)行速度提升上萬(wàn)倍,這個(gè)裝飾器就是@functools.lru_cache。
以比較知名的斐波那契數(shù)列的例子來(lái)演示一下。
由于它遞歸計(jì)算的過(guò)程中,還會(huì)用到之前計(jì)算的結(jié)果,因此會(huì)涉及較多的重復(fù)計(jì)算,下面先看一下正常計(jì)算的耗時(shí)情況。
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時(shí),耗時(shí)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
耗時(shí)為1.811981e-05,足足差了4個(gè)量級(jí),快了10000+倍!
到此這篇關(guān)于Python裝飾器有哪些絕妙的用法的文章就介紹到這了,更多相關(guān)Python裝飾器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python實(shí)現(xiàn)一次性封裝多條sql語(yǔ)句(begin end)
這篇文章主要介紹了python實(shí)現(xiàn)一次性封裝多條sql語(yǔ)句(begin end),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-06-06
Python利用jmespath模塊進(jìn)行json數(shù)據(jù)處理
jmespath是python的第三方模塊,是需要額外安裝的。它在python原有的json數(shù)據(jù)處理上做出了很大的貢獻(xiàn)。本文將詳細(xì)介紹如何利用jmespath實(shí)現(xiàn)json數(shù)據(jù)處理,需要的可以參考一下2022-03-03
python3+pyqt5+itchat微信定時(shí)發(fā)送消息的方法
今天小編就為大家分享一篇python3+pyqt5+itchat微信定時(shí)發(fā)送消息的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-02-02
Python編程二分法實(shí)現(xiàn)冒泡算法+快速排序代碼示例
這篇文章主要介紹了Python編程二分法實(shí)現(xiàn)冒泡算法+快速排序代碼示例,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-01-01
詳解python3 + Scrapy爬蟲(chóng)學(xué)習(xí)之創(chuàng)建項(xiàng)目
這篇文章主要介紹了python3 Scrapy爬蟲(chóng)創(chuàng)建項(xiàng)目,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
Python批量對(duì)word文檔進(jìn)行操作步驟
這篇文章主要介紹了Python批量對(duì)word文檔進(jìn)行操作,一步步逐步完成創(chuàng)建文檔,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-02-02
Python?SQLAlchemy庫(kù)的實(shí)現(xiàn)示例
SQLAlchemy庫(kù)是一個(gè)強(qiáng)大的工具,為開(kāi)發(fā)人員提供了便捷的方式來(lái)處理與數(shù)據(jù)庫(kù)的交互,本文主要介紹了Python?SQLAlchemy庫(kù)的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的可以了解一下2024-06-06

