python中的装饰器-(重复阅读)
---1---
假設(shè)我們要增強(qiáng)某個(gè)函數(shù)的功能,比如,在函數(shù)調(diào)用前后自動(dòng)打印日志,但又不希望修改某個(gè)函數(shù)的定義,這種在代碼運(yùn)行期間動(dòng)態(tài)增加功能的方式,稱之為“裝飾器”(Decorator).
裝飾器本質(zhì)上是一個(gè)Python函數(shù),它可以讓其他函數(shù)在不需要做任何代碼變動(dòng)的前提下增加額外功能,裝飾器的返回值也是一個(gè)函數(shù)對(duì)象。它經(jīng)常用于有切面需求的場(chǎng)景,比如:插入日志、性能測(cè)試、事務(wù)處理、緩存、權(quán)限校驗(yàn)等場(chǎng)景。裝飾器是解決這類問題的絕佳設(shè)計(jì),有了裝飾器,我們就可以抽離出大量與函數(shù)功能本身無關(guān)的雷同代碼并繼續(xù)重用。概括的講,裝飾器的作用就是為已經(jīng)存在的對(duì)象添加額外的功能。
---2---
本質(zhì)上,decorator就是一個(gè)返回函數(shù)的高階函數(shù)。所以,我們要定義一個(gè)能打印日志的decorator.
觀察下面的代碼:
?
把@log放到now()函數(shù)的定義處,相當(dāng)于執(zhí)行了語句:
now = log(now)由于log()是一個(gè)decorator,返回一個(gè)函數(shù),所以,原來的now()函數(shù)仍然存在,只是現(xiàn)在同名的now變量指向了新的函數(shù),于是調(diào)用now()將執(zhí)行新函數(shù),即在log()函數(shù)中返回的wrapper()函數(shù)。
wrapper()函數(shù)的參數(shù)定義是(*args, **kw),因此,wrapper()函數(shù)可以接受任意參數(shù)的調(diào)用。在wrapper()函數(shù)內(nèi),首先打印日志,再緊接著調(diào)用原始函數(shù)。
---3---
如果decorator本身需要傳入?yún)?shù),那就需要編寫一個(gè)返回decorator的高階函數(shù),寫出來會(huì)更復(fù)雜。比如,要自定義log的文本:
def log(text):def decorator(func):def wrapper(*args, **kw):print('%s %s():' % (text, func.__name__))return func(*args, **kw)return wrapperreturn decorator這個(gè)3層嵌套的decorator用法如下:
@log('execute') def now():print('2015-3-25')和兩層嵌套的decorator相比,3層嵌套的效果是這樣的:
>>> now = log('execute')(now)我們來剖析上面的語句,首先執(zhí)行l(wèi)og('execute'),返回的是decorator函數(shù),再調(diào)用返回的函數(shù),參數(shù)是now函數(shù),返回值最終是wrapper函數(shù)。
以上兩種decorator的定義都沒有問題,但還差最后一步。因?yàn)槲覀冎v了函數(shù)也是對(duì)象,它有__name__等屬性,但你去看經(jīng)過decorator裝飾之后的函數(shù),它們的__name__已經(jīng)從原來的'now'變成了'wrapper':
>>> now.__name__ 'wrapper'因?yàn)榉祷氐哪莻€(gè)wrapper()函數(shù)名字就是'wrapper',所以,需要把原始函數(shù)的__name__等屬性復(fù)制到wrapper()函數(shù)中,否則,有些依賴函數(shù)簽名的代碼執(zhí)行就會(huì)出錯(cuò)。
不需要編寫wrapper.__name__ = func.__name__這樣的代碼,Python內(nèi)置的functools.wraps就是干這個(gè)事的,所以,一個(gè)完整的decorator的寫法如下:
import functoolsdef log(func):@functools.wraps(func)def wrapper(*args, **kw):print('call %s():' % func.__name__)return func(*args, **kw)return wrapper或者針對(duì)帶參數(shù)的decorator:
import functoolsdef log(text):def decorator(func):@functools.wraps(func)def wrapper(*args, **kw):print('%s %s():' % (text, func.__name__))return func(*args, **kw)return wrapperreturn decorator上面的東西看的一知半解的,看完下面的這一段應(yīng)該會(huì)好點(diǎn):
def check_is_admin(f):def wrapper(*args,**kwargs):if kwargs.get('username') != 'admin':raise Exception("This user is not allowed to get food")return f(*args, **kwargs)return wrapperclass Storage(object):@check_is_admindef get_food(self, username,food):return Storage.get(food)@check_is_admindef put_food(self,username,food):return Storage.put(food)?
參考:
(1).https://www.liaoxuefeng.com/wiki/1016959663602400/1017451662295584(廖雪峰官方文檔)
(2).https://www.zhihu.com/question/21408921/answer/129036707?(怎樣才能寫出 Pythonic 的代碼?)
(3).https://www.zhihu.com/question/26930016/answer/99243411? ?(如何理解Python裝飾器?)
?
?
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的python中的装饰器-(重复阅读)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: shiro学习(16):使用注解实现权限
- 下一篇: python有哪些常用的库