裝飾器

裝飾器

一泪电、什么是裝飾器

???????? ”裝飾“代指為被裝飾對(duì)象添加新的功能,“器”代指器具/工具懦鼠,裝飾器與被裝飾的對(duì)象均可以是任意可調(diào)用對(duì)象钻哩。概括地講屹堰,裝飾器的作用就是在不修改被裝飾對(duì)象源代碼和調(diào)用方式的前提下為被裝飾對(duì)象添加額外的功能。裝飾器經(jīng)常用于有切面需求的場(chǎng)景街氢,比如:插入日聲扯键、性能測(cè)試、事務(wù)處理珊肃、緩存荣刑、權(quán)限校驗(yàn)等應(yīng)用場(chǎng)景,裝飾器是解決這類問題的絕佳設(shè)計(jì)伦乔,有了裝飾器厉亏,就可以抽離出大量與函數(shù)功能本身無關(guān)的協(xié)同代碼并繼續(xù)重用。

???????? 可調(diào)用對(duì)象有函數(shù)烈和,方法或者類爱只,此處我們單以本章主題函數(shù)為例,來介紹函數(shù)裝飾器斥杜,并且被裝飾的對(duì)象也是函數(shù)虱颗。

二、為何要用裝飾器

???????? 軟件的設(shè)計(jì)應(yīng)該遵循開放封閉原則蔗喂,即對(duì)擴(kuò)展是開放的忘渔,而對(duì)修改是封閉的。對(duì)擴(kuò)展開放缰儿,意味著有新的需求或變化時(shí)畦粮,可以對(duì)現(xiàn)有代碼進(jìn)行擴(kuò)展,以適應(yīng)新的情況乖阵。對(duì)修改封閉宣赔,意味著對(duì)象一旦設(shè)計(jì)完成,就可以獨(dú)立完成其工作瞪浸,而不要對(duì)其進(jìn)行修改儒将。

三、裝飾器的實(shí)現(xiàn)

函數(shù)飾器分為:無參裝飾器和有參裝飾兩種对蒲,二者的實(shí)現(xiàn)原理一樣钩蚊,都是“函數(shù)嵌套+閉包+函數(shù)對(duì)象”的組合使用的產(chǎn)物。

1.無參裝飾器的實(shí)現(xiàn)

???????? 如果想為下述函數(shù)添加統(tǒng)計(jì)其執(zhí)行時(shí)間的功能

???????? impor time

???????? def index():

?????????????????? time.sleep(3)

?????????????????? print(“welcome to the indexpage”)

?????????????????? return 200

???????? index()

遵循不修改被裝飾對(duì)象對(duì)源代碼的原則蹈矮,我們想到的解決方法可能是這樣

???????? start_time = time.time()

???????? index(x)

???????? stop_time = time.time()

???????? print(“run time is %s”%(stop_time-start_time))


???????? wrapper(index)

???????? wrapper(其他函數(shù))


這便違反了不能修改被裝飾對(duì)象調(diào)用方式的原則砰逻,于是我們換一種為函數(shù)體傳值的方式,即將值包給函數(shù)

def timer(func):

???????? def wrapper():

?????????????????? start_time = time.time()

?????????????????? res = func()

?????????????????? stop_time=time.time()

?????????????????? print(“run time is %s”%(stop_time-start_time))

?????????????????? return res

???????? return wrapper

這倦我們便可以在不修改被裝飾函數(shù)源代碼和調(diào)用方式的前提下為其加上統(tǒng)計(jì)時(shí)間的功能泛鸟,只不過需要事先執(zhí)行一次time將被裝飾的函數(shù)傳入蝠咆,返回一個(gè)閉包函數(shù)wrapper重新賦值給變量名/函數(shù)名index

???????? index = timer(index)

???????? index()

修正裝飾器time如下

def timer(fcun):

???????? def wrpper(*args,**kwargs):

?????????????????? start_time = time.time()

?????????????????? res = fun(*args,**kwargs)

?????????????????? stop_time = time.time()

?????????????????? print(“run time is %s”%(stop_time-start_time))

?????????????????? return res

???????? return wrapper


???????? 為了簡(jiǎn)潔而優(yōu)雅地使用裝飾器python提供了專門的裝飾器語法來取代index=timer(index)的形式,需要在被裝飾對(duì)象的正上方單獨(dú)一行添加@timer北滥,當(dāng)解釋器到@timer時(shí)就會(huì)調(diào)用timer函數(shù)刚操,且把它正下方的函數(shù)名當(dāng)做實(shí)參傳入闸翅,然后將返回的結(jié)果重新賦值給原函數(shù)名

???????? @timer

???????? def index():

?????????????????? time.sleep(3)

?????????????????? print(“welcome to the indexpage”)

?????????????????? return 200

???????? @timer

?????????????????? time.sleep(5)

?????????????????? print(“welcome to the homepage”,name)

???????? 如果我們有多個(gè)裝飾器,可以疊加多個(gè)

???????? @deco3

???????? @deco2

???????? @deco1

???????? def index():

?????????????????? pass

???????? 2.有參裝飾器的實(shí)現(xiàn)

???????? 了解無參裝飾器的實(shí)現(xiàn)原理后赡茸,我們可以再實(shí)現(xiàn)一個(gè)用來為被裝飾對(duì)象添加認(rèn)證功能的裝飾器缎脾,實(shí)現(xiàn)的基本形式如下

???????? def deco(func):

?????????????????? def wrapper(*args,**kwargs):

??????????????????????????? if driver =? “file”:

???????????????????????????????????? 編寫基于文件的認(rèn)證,認(rèn)證通過則執(zhí)行res=fun(*args,**kwargs),并返回???????????????????????????????? res

??????????????????????????? elif driver == “mysql”:

?????????????????? retun wrapper


函數(shù)wrapper需要一個(gè)driver參數(shù)占卧,而函數(shù)deco與wrapper的參數(shù)都有其特定的功能遗菠,不能用來接受其他類別的參數(shù),可以在deco的外部再包一層函數(shù)auth,用來專門接受處的參數(shù)华蜒,這樣便保證了在auth函數(shù)內(nèi)無論多少層都可以引用到

此時(shí)我們就實(shí)現(xiàn)了一個(gè)有參裝飾器

先調(diào)用auth_type(driver=”file’),得到@deco,deco是個(gè)閉包函數(shù)

包含了對(duì)外部作用域名字driver的引用辙纬,@deco的語法意義與無參裝飾器一樣

@auth(driver=”file”)

Def index():

???????? Pass

@auth(driver=”mysql”)

Def home():

???????? Pass

可以使用help(函數(shù)名)來查看函數(shù)的文檔注釋,本質(zhì)就是查看函數(shù)的doc屬性叭喜,但對(duì)于被裝飾之后的函數(shù)贺拣,查看文檔注釋

@timer

def home(name):

???????? time.sleep(5)

???????? print(“welcome to the home page”,name)

print(help(home))

在被裝飾之后home = wrapper,查看home.name也可以發(fā)現(xiàn)home的函數(shù)名確實(shí)是wrapper,想要保留原函數(shù)的文檔和函數(shù)名屬性,需要修正裝飾器

def timer(func):

???????? def wrapper(*argw,**kwargs):

?????????????????? start_time=time.time()

?????????????????? res=func(*args,**kwargs)

?????????????????? stop_time=time.time()

?????????????????? print(“run time is%s”,%(stop_time-start_time))

?????????????????? return res

???????? wrapper._doc_=func._doc_

???????? wrapper._name_=func._name_

???????? return wrapper

按照上述方式來實(shí)現(xiàn)保留原函數(shù)屬性過于麻煩捂蕴,functools模塊下提供一個(gè)裝飾器wraps專門用來幫我們實(shí)現(xiàn)這件事

???????? from functools import wraps

???????? def timer(func):

?????????????????? @wraps(func)

?????????????????? def wrapper(*args,**kwargs):

??????????????????????????? start_time=time.time()

??????????????????????????? res = func(*args,**kwargs)

??????????????????????????? stop_time=time.time()

??????????????????????????? print(“run time is%s:%(stop_time-start_time))

??????????????????????????? return res

?????????????????? return wrapper

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末譬涡,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子啥辨,更是在濱河造成了極大的恐慌涡匀,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件溉知,死亡現(xiàn)場(chǎng)離奇詭異陨瘩,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)级乍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門舌劳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人玫荣,你說我怎么就攤上這事甚淡。” “怎么了捅厂?”我有些...
    開封第一講書人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵材诽,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我恒傻,道長(zhǎng),這世上最難降的妖魔是什么建邓? 我笑而不...
    開封第一講書人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任盈厘,我火速辦了婚禮,結(jié)果婚禮上官边,老公的妹妹穿的比我還像新娘沸手。我一直安慰自己外遇,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開白布契吉。 她就那樣靜靜地躺著跳仿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪捐晶。 梳的紋絲不亂的頭發(fā)上菲语,一...
    開封第一講書人閱讀 51,554評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音惑灵,去河邊找鬼山上。 笑死,一個(gè)胖子當(dāng)著我的面吹牛英支,可吹牛的內(nèi)容都是我干的佩憾。 我是一名探鬼主播,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼干花,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼妄帘!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起池凄,我...
    開封第一講書人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤抡驼,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后修赞,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體婶恼,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年柏副,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了勾邦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡割择,死狀恐怖眷篇,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情荔泳,我是刑警寧澤蕉饼,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站玛歌,受9級(jí)特大地震影響昧港,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜支子,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一创肥、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦叹侄、人聲如沸巩搏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽贯底。三九已至,卻和暖如春撒强,著一層夾襖步出監(jiān)牢的瞬間禽捆,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工尿褪, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留睦擂,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓杖玲,卻偏偏與公主長(zhǎng)得像顿仇,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子摆马,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 在學(xué)習(xí)Python的過程中臼闻,我相信有很多人和我一樣,對(duì)Python的裝飾器一直覺得很困惑囤采,我也是困惑了好久述呐,并通過...
    愚灬墨閱讀 460評(píng)論 1 1
  • 裝飾器函數(shù) 楔子 作為一個(gè)會(huì)寫函數(shù)的python開發(fā)乓搬,我們從今天開始要去公司上班了。寫了一個(gè)函數(shù)代虾,就交給其他開發(fā)用...
    go以恒閱讀 273評(píng)論 0 0
  • 裝飾器本質(zhì)上是一個(gè)函數(shù)进肯,該函數(shù)用來處理其他函數(shù),它可以讓其他函數(shù)在不需要修改代碼的前提下增加額外的功能棉磨,裝飾器的返...
    胡一巴閱讀 414評(píng)論 0 0
  • 一江掩、裝飾器的基本使用 在不改變函數(shù)源代碼的前提下,給函數(shù)添加新的功能乘瓤,這時(shí)就需要用到“裝飾器”环形。 0.開放封閉原則...
    NJingZYuan閱讀 529評(píng)論 0 0
  • 本文的目錄如下:裝飾器語法糖入門用法:日志打印器入門用法:時(shí)間計(jì)時(shí)器進(jìn)階用法:帶參數(shù)的函數(shù)裝飾器高階用法:不帶參數(shù)...
    A遇上方知友閱讀 748評(píng)論 0 0