Python裝飾器總結(jié),帶你幾步跨越此坑!
關(guān)于訴求
昨天簡(jiǎn)單聊了下Flask的學(xué)習(xí)感想,然后分享了一些Flask的學(xué)習(xí)方式與視頻。其中提到在學(xué)習(xí)Python Web前,請(qǐng)先將python基礎(chǔ)及裝飾器等知識(shí)有一個(gè)了解,這樣學(xué)習(xí)起來(lái)不至于太過(guò)吃力。
然后,今天有朋友私信說(shuō)對(duì)python的類和裝飾器不甚了解,希望能講講這些知識(shí)。關(guān)于函數(shù)、方法、類,我之前發(fā)過(guò)一篇文章,就不再贅述了。其實(shí)去年詳細(xì)總結(jié)過(guò)一篇關(guān)于Python裝飾器的文章,只不過(guò)是在公司博客寫的,沒(méi)辦法復(fù)制出來(lái),所以今天就這之前的知識(shí)做一個(gè)總結(jié)和復(fù)習(xí)吧。
引子
談及python裝飾器,很多人第一時(shí)間想到的是@這個(gè)符號(hào)。在方法上一行加上@decorator就行了。但很多人看著都會(huì)、一用就跪,而且很多時(shí)候,我們都不知道什么場(chǎng)景下適合使用裝飾器。那么今天就帶大家一步步了解裝飾器的使用吧
裝飾器(Decorator)是python的一個(gè)重要部分,簡(jiǎn)單來(lái)說(shuō),他是修改其他函數(shù)功能的函數(shù)。
他們有助于讓我們的代碼更簡(jiǎn)短,也更Pythonic!
萬(wàn)物皆對(duì)象
在Python的世界中,萬(wàn)物皆對(duì)象,聽起來(lái)比較抽象,但其實(shí)理解起來(lái)很簡(jiǎn)單,你可以用將任何一個(gè)變量、函數(shù)、方法、類等等賦值給另一個(gè)變量。只有你了解了這些,才能進(jìn)一步的理解裝飾器。
函數(shù)賦值
而學(xué)習(xí)裝飾器前,我們先來(lái)舉一個(gè)簡(jiǎn)單的函數(shù)例子:
def hello(name='BreezePython'):
return "say hello to {}".format(name)
hello()
# 'say hello to BreezePython'
hi = hello
hi('tommorow')
# 'say hello to tommorow'
我們首先創(chuàng)建一個(gè)hello的函數(shù),然后調(diào)用,之后將hello賦值給hi,之后調(diào)用hi,完成了相同的操作,也許你覺(jué)得這個(gè)例子so easy,那就接著往下走。
函數(shù)嵌套
當(dāng)我們?cè)诤瘮?shù)中再次定義一個(gè)函數(shù)是,即完成了一個(gè)函數(shù)的嵌套操作:
def hello():
print('is hello function...')
def hi():
return 'hi ,nice to meet you!'
def bye():
return 'bye,stranger...'
print(hi())
print(bye())
hello()
output:
is hello function...
hi ,nice to meet you!
bye,stranger...
hi()
output:
Traceback (most recent call last):
File "", line 1, in
NameError: name 'hi' is not defined
上面的例子看到了,函數(shù)中可以調(diào)用子函數(shù),但如果你直接去調(diào)用子函數(shù),則會(huì)拋出未定義的異常,那么我們?nèi)绾握{(diào)用子函數(shù)?
函數(shù)中返回函數(shù)
讓我們由淺入深,先考慮從函數(shù)中返回函數(shù)
def hello(name=None):
print('is hello function...')
def hi():
return 'hi ,nice to meet you!'
def bye():
return 'bye,stranger...'
if name == 'BreezePython':
return hi
else:
return bye
main = hello('BreezePython')
>>> output: is hello function...
main()
>>> output: 'hi ,nice to meet you!'
初學(xué)者可能對(duì)python中的小括號(hào)有些迷糊,添加小括號(hào)與否有什么影響呢?
函數(shù)作為參數(shù)傳遞
def child():
return 'is child function...'
def main(func):
print('is main function...')
print(func())
main(child)
output:
>>> is main function...
>>> is child function...
我們新建了一個(gè)child函數(shù),然后將child話術(shù)傳遞給main函數(shù),在main函數(shù)中調(diào)用child函數(shù),達(dá)到了將函數(shù)作為參數(shù)傳遞的結(jié)果。
python閉包
我們先將上面的函數(shù)嵌套與傳參來(lái)進(jìn)行一下合并:
def main(func):
print('is main function...')
def child():
print('it print before exec func...')
func()
print('it print after exec func...')
return child
def alone():
print("I'm ?alone function ...")
fix = main(alone)
fix()
output:
>>> is main function...
>>> it print before exec func...
>>> I'm ?alone function ...
>>> it print after exec func...
通過(guò)合并,我們將上面兩個(gè)例子進(jìn)行和組裝,變成了一個(gè)升級(jí)版的閉包,很多人會(huì)說(shuō),什么是閉包呢?其實(shí)很簡(jiǎn)單…
first Decorator
上面的例子中,我們看到了一個(gè)閉包與函數(shù)傳參的例子,那么裝飾器是什么?其實(shí)就是閉包+函數(shù)傳參,如果上面的例子你看懂了,那么現(xiàn)在你只需要對(duì)代碼格式稍作修改,就變成了一個(gè)裝飾器!
def main(func):
print('is main function...')
def child():
print('it print before exec func...')
func()
print('it print after exec func...')
return child
@main
def alone():
print("I'm ?alone function ...")
alone()
# output:
>>> is main function...
>>> it print before exec func...
>>> I'm ?alone function ...
>>> alone
>>> it print after exec func...
我們只是修改了一行代碼的格式,就轉(zhuǎn)化成了裝飾器,@main就代表func = main(func)
可這樣就算完美的裝飾器了么?NO….
我們將alone函數(shù)稍作變更,即可看出問(wèn)題所在:
def alone():
print("I'm ?alone function ...")
print(alone.__name__)
正常情況下,調(diào)用alone帶引的alone.__name__就是函數(shù)名即alone,但如果我們是通過(guò)裝其實(shí)調(diào)用后打印呢,結(jié)果是什么?相信大家能猜到,是child。child是main函數(shù)的內(nèi)建函數(shù),它重寫了我們的函數(shù)名,如何解決這個(gè)問(wèn)題呢?
from functools import wraps
# first Decorator
def main(func):
print('is main function...')
@wraps(func)
def child():
print('it print before exec func...')
func()
print('it print after exec func...')
return child
@main
def alone():
print("I'm ?alone function ...")
print(alone.__name__)
alone()
# output:
>>> is main function...
>>> it print before exec func...
>>> I'm ?alone function ...
>>> alone
>>> it print after exec func...
我們通過(guò)引入functools方法中的wraps,保證了函數(shù)名稱的原始性
@wraps接受一個(gè)函數(shù),進(jìn)行裝飾,并加入了復(fù)制函數(shù)名稱、注釋文檔、參數(shù)列表等功能,這樣可以是我們?cè)谘b飾器里面訪問(wèn)在裝飾之前的函數(shù)的屬性
裝飾器實(shí)例
裝飾器比大量的使用在Flask、Django中,學(xué)好了它不管是對(duì)于你理解flask的路由,還是之后的代碼開發(fā)都有很多幫助,那么我們來(lái)做個(gè)簡(jiǎn)單的例子,日志打印裝飾器:
import time
from functools import wraps
def log_level(level='DEBUG'):
def log_format(func):
@wraps(func)
def format(*args, **kwargs):
logtime = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
print("[{}]{}: ".format(level, logtime), end='')
return func(*args, **kwargs)
return format
return log_format
@log_level()
def log1():
print("Hello,Welcome to 清風(fēng)Python...")
@log_level('ERROR')
def log2():
print("清風(fēng)Python 是我的公眾號(hào)...")
log1()
time.sleep(1)
log2()
# output:
>>> [DEBUG]2019-08-20 01:24:23: Hello,Welcome to 清風(fēng)Python...
>>> [ERROR]2019-08-20 01:24:24: 清風(fēng)Python 是我的公眾號(hào)...
類的裝飾器
講了這么多,本來(lái)覺(jué)得該結(jié)束了,可總覺(jué)得還差點(diǎn)什么!沒(méi)錯(cuò),我們只是講到了函數(shù)的裝飾器,那么類的裝飾器該如何操作呢?
import time
from functools import wraps
class Logger:
def __init__(self,level='DEBUG'):
self.level = level
def __call__(self, func):
@wraps(func)
def log_format(*args, **kwargs):
log_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
print("[{}]{}: ".format(self.level, log_time), end='')
return func(*args, **kwargs)
return log_format
@Logger()
def log1():
print("Hello,Welcome to 清風(fēng)Python...")
@Logger('Error')
def log2():
print("清風(fēng)Python 是我的公眾號(hào)...")
log1()
time.sleep(1)
log2()
# output:
>>> [DEBUG]2019-08-20 01:24:23: Hello,Welcome to 清風(fēng)Python...
>>> [ERROR]2019-08-20 01:24:24: 清風(fēng)Python 是我的公眾號(hào)...
今天的裝飾器內(nèi)容就分享到這里吧…
The End
OK,今天的內(nèi)容就到這里,歡迎大家關(guān)注“清風(fēng)Python”公眾號(hào)
python
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。