一、作為一個(gè)測(cè)試為什么學(xué)裝飾器?
在使用python寫東西的時(shí)候,可能會(huì)遇到并使用到裝飾器,為了加深“功力”還是很有必要去學(xué)習(xí)一下的。不能只知道使用,完全去“復(fù)制”別人的代碼,還是要知道為什么要這么寫的。以后可能親自去實(shí)現(xiàn)一個(gè)裝飾器的機(jī)會(huì)并不多,所以也許并不需要能熟練的去寫一個(gè)裝飾器。為了滿足“好奇心”了解一下還是很有不錯(cuò)的學(xué)習(xí)過程。
二、什么是裝飾器
裝飾器本質(zhì)上是一個(gè)python函數(shù),它可以讓其他函數(shù)在不需要做任何代碼變動(dòng)的前提下增加額外功能。
這是我查閱資料時(shí),看到比較多的描述,所以裝飾器它是:
- 它也是一個(gè)函數(shù)
- 能夠給修飾的代碼提供額外的功能
這讓我想到了java中的“注解”,當(dāng)然也許他們的實(shí)現(xiàn)方式不同。(若類比失當(dāng),大佬勿噴,留言指正)
都類似@xxxx的語(yǔ)法糖寫法。
三、 典型的應(yīng)用場(chǎng)景
一般應(yīng)用于抽離出與函數(shù)邏輯無(wú)關(guān)的可重用的代碼,比如插入日志、性能測(cè)試、事務(wù)處理、緩存、權(quán)限驗(yàn)證等場(chǎng)景。
舉個(gè)例子:
- 在flask框架中,來(lái)做路由的定義
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run()
- 在單元測(cè)試中,可以用來(lái)忽略某些測(cè)試case
@unittest.skip("showing class skipping")
class MySkippedTestCase(unittest.TestCase):
def test_not_run(self):
pass
四、最簡(jiǎn)單的一種自定義裝飾器
裝飾器無(wú)參數(shù),被裝飾的函數(shù)也是無(wú)參數(shù)的,是最基本的一種自定義參數(shù)。
最常被用來(lái)舉例的場(chǎng)景:計(jì)算某個(gè)函數(shù)的運(yùn)行耗時(shí)
- 先看下不用裝飾器的寫法吧
import time
def foo():
for i in range(10):
print('....')
time.sleep(0.5)
start_time = time.time()
foo()
end_time = time.time()
print('spend time is {}'.format(end_time - start_time))
不多介紹了吧。
但是現(xiàn)在需要你再寫一個(gè)函數(shù),也是計(jì)算其的耗時(shí),你難道還有在寫一遍嗎?
所以我們可以把重復(fù)的代碼剝離出來(lái),以往的做法可能是抽取公共方法,但是這種邏輯你會(huì)發(fā)現(xiàn)有點(diǎn)無(wú)從下手,重復(fù)的代碼在你的邏輯前后,這個(gè)時(shí)候裝飾器的作用就出來(lái)了。
import time
def show_time(func):
def wrapper():
start_time = time.time()
func()
end_time = time.time()
print('spend time is {}'.format(end_time - start_time))
return wrapper
@show_time
def foo():
for i in range(5):
print('*****')
time.sleep(0.5)
@show_time
def foo_copy():
for i in range(5):
print('+++++')
time.sleep(0.5)
foo()
foo_copy()
看一下打印結(jié)果感受一下:
*****
*****
*****
*****
*****
spend time is 2.5002501010894775
+++++
+++++
+++++
+++++
+++++
spend time is 2.5002498626708984
感覺有點(diǎn)不錯(cuò)。
五、被裝飾函數(shù)帶參數(shù)的裝飾器
本例是被裝飾的函數(shù)帶兩個(gè)參數(shù),而裝飾器函數(shù)不帶參數(shù)的,有點(diǎn)簡(jiǎn)單,代碼如下:
import time
def show_time(func):
def wrapper(a, b):
start_time = time.time()
func(a, b)
end_time = time.time()
print('spend time is {}'.format(end_time - start_time))
return wrapper
@show_time
def foo(a, b):
for i in range(5):
print('a + b = ' + str(a + b))
time.sleep(0.5)
foo(1, 2)
打印結(jié)果:
a + b = 3
a + b = 3
a + b = 3
a + b = 3
a + b = 3
spend time is 2.5002501010894775
六、裝飾器帶參數(shù)的情況
就是再使用一個(gè)帶參數(shù)的函數(shù),將裝飾器再做一層封裝,返回一個(gè)裝飾器函數(shù)。在裝飾器函數(shù)中就可以使用這個(gè)傳進(jìn)來(lái)的參數(shù),這種函數(shù)好像也叫做閉包函數(shù)。(才疏學(xué)淺,不做解釋)
舉例代碼:
import time
def is_show_time(is_show=True):
def show_time(func):
def wrapper(a, b):
start_time = time.time()
func(a, b)
end_time = time.time()
if is_show is True:
print('spend time is {}'.format(end_time - start_time))
return wrapper
return show_time
@is_show_time(is_show=True)
def foo(a, b):
for i in range(5):
print('a + b = ' + str(a + b))
time.sleep(0.5)
@is_show_time(is_show=False)
def foo_copy(a, b):
for i in range(5):
print('a + b = ' + str(a + b))
time.sleep(0.5)
foo(1, 2)
foo_copy(3, 4)
is_show_time函數(shù)將我們上面例子中的show_time函數(shù)做了封裝,透出一個(gè)參數(shù)用來(lái)控制是否執(zhí)行打印語(yǔ)句。
foo和foo_copy分別傳入兩種參數(shù)值來(lái)測(cè)試一下是否符合預(yù)期。
打印結(jié)果如下:
a + b = 3
a + b = 3
a + b = 3
a + b = 3
a + b = 3
spend time is 2.5
a + b = 7
a + b = 7
a + b = 7
a + b = 7
a + b = 7
Process finished with exit code 0
七、后記
介紹上面這些,應(yīng)該對(duì)裝飾器有一個(gè)大概的了解,想要更深的去了解,可以多利用搜索引擎。
感謝:whyaza的分享