国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
Python學(xué)習(xí)—裝飾器

學(xué)習(xí)Python已經(jīng)有一段時間了,陸續(xù)學(xué)了一些基礎(chǔ)部分,但是理解的不是很深刻,每過一段時間就會忘記,所以不得不寫一些博客進(jìn)行記錄,加深自己的理解。這兩個星期一直在研究裝飾器,開始覺得很簡單,但是只知其然,真要寫一個裝飾器卻又不知如何下手了,顯然并沒有真正理解裝飾器的實(shí)現(xiàn)過程。經(jīng)過兩個星期的學(xué)習(xí)終于覺得自己對裝飾器的理解達(dá)到了一定的深度,因此 必須記錄一下。那就開始吧!

為什么需要裝飾器:存在一種需求就是,有一個函數(shù),在多個地方使用或者提供給項(xiàng)目組的他人使用,現(xiàn)在需要為此函數(shù)增加一些功能,但是你不能修改函數(shù)的代碼,也不能重寫一個替代函數(shù),然后讓別人重新使用你的替代函數(shù),怎么辦?裝飾器就是干這個的。

裝飾器:本質(zhì)是一個函數(shù),可在不改變原函數(shù)代碼與調(diào)用方式的情況下,為函數(shù)增加功能。

解裝飾器的實(shí)現(xiàn)需要以下幾個概念:

1、函數(shù)名就是一個變量,使用def方式定義一個函數(shù)時,就是將函數(shù)名與函數(shù)體建立一個指向關(guān)系,函數(shù)名對應(yīng)的值就是函數(shù)體在內(nèi)存中的地址;既然函數(shù)名是一個變量,那它就具有變量的特性,可以被修改,注意不是函數(shù)名被修改,而是函數(shù)名對應(yīng)的值可以被修改

2、高階函數(shù):高階函數(shù)就是把函數(shù)當(dāng)做參數(shù)傳遞,因?yàn)楹瘮?shù)名也是變量,所以可把函數(shù)名當(dāng)做參數(shù)傳遞,又因?yàn)楹瘮?shù)名對應(yīng)的值是是函數(shù)體,因此可以通過傳入的函數(shù)名參數(shù)使用傳入的函數(shù)。

3、函數(shù)閉包:把函數(shù)名當(dāng)做一個值返回,成為函數(shù)閉包;一般在一個函數(shù)的再部在定義一個函數(shù),那么在外層函數(shù)的外面是不可以使用函數(shù)內(nèi)的局部函數(shù)的,但是利用閉包,把內(nèi)部函數(shù)以函數(shù)名作為返回值,那么就可以做到在函數(shù)外使用內(nèi)部函數(shù)。關(guān)于函數(shù)的閉包,將另寫一份博客。

下面詳細(xì)說明裝飾器的實(shí)現(xiàn)過程:

  1. import time
  2. def luke():
  3. time.sleep(2)
  4. print('in function luke')

現(xiàn)在要實(shí)現(xiàn)一個裝飾器,在執(zhí)行l(wèi)uke函數(shù)時計算其執(zhí)行時間

既然裝飾器要裝飾一個函數(shù),就必須有一個參數(shù),這個參數(shù)用來接收被裝飾的函數(shù)的函數(shù)名

第一步定義一個裝飾器函數(shù),此函數(shù)有一個參數(shù)func,func用于接收一個函數(shù)名

  1. def caculate_func_run_time(func):
  2. pass

第二部實(shí)現(xiàn)函數(shù)執(zhí)行的時間計算:

  1. def caculate_func_run_time(func):
  2. start_time = time.time()
  3. func()
  4. end_time = time.time()
  5. print('func run time is %s'%(end_time - start_time))

第三步,利用函數(shù)閉包,在裝飾器內(nèi)部定義一個函數(shù),將函數(shù)體包裝,然后返回裝飾器內(nèi)部的包裝函數(shù)warpper

  1. def caculate_func_run_time(func):
  2. def warpper():
  3. start_time = time.time()
  4. func()
  5. end_time = time.time()
  6. print('func run time is %s'%(end_time - start_time))
  7. return warpper # 注意此處返回的是函數(shù)名,沒有'()'

第四步,利用函數(shù)名即變量的特性,修改luke函數(shù)名對應(yīng)的值

luke = caculate_func_run_time(luke)

經(jīng)過此步之后,luke函數(shù)名不變,但是其值對應(yīng)的已不再是原始函數(shù)體,而是caculate_func_run_time函數(shù)內(nèi)部包裝后的返         回值即warpper函數(shù)名對應(yīng)的值。

luke = caculate_func_run_time(luke)

luke()

以上兩步調(diào)用執(zhí)行時會打印luke函數(shù)執(zhí)行時間,這樣雖然沒有改變luke原始函數(shù)代碼,但是改變了函數(shù)調(diào)用方式,即需要先一步執(zhí)行l(wèi)uke = caculate_func_run_time(luke),然后執(zhí)行l(wèi)uke(),才能達(dá)到需要的效果,怎么辦呢?

python的裝飾器(語法糖)就派上用處了,使用以下語法:

  1. @caculate_func_run_time # 此語法的含義是: luke = caculate_func_run_time(luke)
  2. def luke():
  3. time.sleep(2)
  4. print('in function luke')

所謂語法糖,我的理解就是,@符號后的函數(shù)名將下一行的函數(shù)名當(dāng)作一顆糖(參數(shù))吃掉(進(jìn)行執(zhí)行過程),然后再吐出(返回)一個函數(shù)名還給你,即相當(dāng)于執(zhí)行這一條語句:luke = caculate_func_run_time(luke)

函數(shù)caculate_func_run_time 已經(jīng)是一個裝飾器了,但是有一個問題,原始函數(shù)若存在參數(shù)或者返回值,現(xiàn)在的裝飾器并不能接收原始函數(shù)的參數(shù)或者返回原始函數(shù)的返回值,怎么辦呢?

我們需要修改裝飾器,使其可以接收原始函數(shù)的參數(shù),并返回原始函數(shù)的返回值

  1. def caculate_func_run_time(func):
  2. def warpper(*args, **kwargs):
  3. start_time = time.time()
  4. res = func(*args, **kwargs)
  5. end_time = time.time()
  6. print('func run time is %s'%(end_time - start_time))
  7. return res
  8. return warpper
  9. @caculate_func_run_time # 此語法的含義是: luke = caculate_func_run_time(luke)
  10. def luke(sleep_time):
  11. time.sleep(sleep_time)
  12. print('in function luke')
  13. return 1
  14. luke(3)

以上裝飾器用res 接收func的執(zhí)行結(jié)果,最后返回,這就做到了返回原始函數(shù)的返回值

下面需要理解的是誰接收的luke函數(shù)的參數(shù)呢?

是warpper函數(shù)接收luke函數(shù)參數(shù),為什么?

@caculate_func_run_time   ,此語法的含義是: luke = caculate_func_run_time(luke),luke函數(shù)名變量的值被修改,那這個值是什么呢?

是caculate_func_run_time(luke)執(zhí)行后的返回值,即warpper

因此luke == warpper

luke(3)函數(shù)調(diào)用就是warpper(3)函數(shù)調(diào)用,所以就是warpper函數(shù)接收了luke函數(shù)的參數(shù)

至于在定義warpper函數(shù)使用(*args, **kwargs),是因?yàn)槭褂媒M參數(shù)與關(guān)鍵字參數(shù)結(jié)合,可以達(dá)到接收任意參數(shù)的效果,

因此以上裝飾器函數(shù)可以裝飾任意函數(shù),達(dá)到計算函數(shù)執(zhí)行時間的效果。

為什么(*args, **kwargs),組參數(shù)與關(guān)鍵字參數(shù)結(jié)合可達(dá)到接收任意參數(shù),請參見python學(xué)習(xí)之函篇的博客。

那么至此還有一個問題,裝飾器可不可以有參數(shù)呢?當(dāng)然可以有,但是怎么實(shí)現(xiàn)呢?

無參裝飾器只是兩層包裝,內(nèi)層使用閉包,要實(shí)現(xiàn)有參裝飾器需要再次使用閉包進(jìn)行第三層包裝,這樣內(nèi)部兩層就可以使用有參裝飾器的參數(shù)。

  1. def caculate_func_run_time(*args, **kwargs)
  2. # 在此函數(shù)內(nèi)部任意位置均可使用裝飾器傳入的參數(shù)(*args, **kwargs)
  3. print(*args, **kwargs)
  4. def out_warpper(func):
  5. def warpper(*args, **kwargs):
  6. start_time = time.time()
  7. res = func(*args, **kwargs)
  8. end_time = time.time()
  9. print('func run time is %s'%(end_time - start_time))
  10. return res
  11. return warpper
  12. return out_warpper
  13. @caculate_func_run_time('有參裝飾器')
  14. def luke(sleep_time):
  15. time.sleep(sleep_time)
  16. print('in function luke')
  17. return 1

@caculate_func_run_time('有參裝飾器') ——語法糖的執(zhí)行步驟是:

1、執(zhí)行:caculate_func_run_time('有參裝飾器'),返回一個out_warpper

2、執(zhí)行:out_warpper(luke),返回一個warpper

3、更新原始函數(shù):luke = warpper

至此就做到了有參裝飾器的實(shí)現(xiàn),三層的裝飾器已經(jīng)可以處理任意參數(shù)的裝飾器,因此也就不再需要第四層的閉包封裝。

那么小伙伴們還想不想了解一下更高級的多級裝飾,這與三層的裝飾器可不一樣哦??纯聪旅娴拇a吧!

小伙伴們覺得應(yīng)該怎樣輸出呢?

  1. def wrapper1(func):
  2. def inner():
  3. print('w1,before')
  4. func()
  5. print('w1,after')
  6. return inner
  7. def wrapper2(func):
  8. def inner():
  9. print('w2,before')
  10. func()
  11. print('w2,after')
  12. return inner
  13. @wrapper2
  14. @wrapper1
  15. def foo():
  16. print('foo')
  17. foo()

下面我們一步步分析吧:

1、當(dāng)python解釋器執(zhí)行@warpper2時,實(shí)質(zhì)是執(zhí)行foo = warpper2(@warpper1),要執(zhí)行此條調(diào)用就必須先執(zhí)行@warpper1,因?yàn)閣arpper2需要一個實(shí)參值

2、執(zhí)行@warpper1就是普通2層裝飾器,將foo函數(shù)包裝在warpper1 內(nèi),由inner返回,我們可以看做是warpper1_innner

3、執(zhí)行warpper2(warpper1_innner),warpper2實(shí)際包裝的是warpper1返回的inner函數(shù),由warpper2 的inner返回,    warpper2內(nèi)的inner函數(shù)執(zhí)行func()時,實(shí)際執(zhí)行的是warpper1的innner函數(shù),返回的函數(shù)我們可以看做是    warpper2_inner,此時foo函數(shù)即為warpper2_inner

4、執(zhí)行foo()函數(shù),即要先執(zhí)行warpper2返回的inner函數(shù),首先輸出“w2,before”;然后執(zhí)行func();此函數(shù)是warpper2的  參數(shù)接收的warpper1的inner函數(shù)

5、執(zhí)行warpper2->inner的func函數(shù),即執(zhí)行warpper1的inner函數(shù),那么先輸出“w1,before”;接著執(zhí)行func函數(shù),此函數(shù)即是warpper1函數(shù)的參數(shù)接收的foo函數(shù),因此接著輸出“foo”,warpper1內(nèi)的inner函數(shù)內(nèi)的func函數(shù)執(zhí)行完畢后,再輸出“w1,after”,至此warpper1的inner函數(shù)執(zhí)行完畢返回,即warpper2內(nèi)的inner函數(shù)內(nèi)的func函數(shù)執(zhí)行完畢返回,接著輸出“w2,after”

因此輸出順序是:

  1. w2,before
  2. w1,before
  3. foo
  4. w1,after
  5. w2,after

朋友們,如果是三級裝飾呢,相信小伙伴們已經(jīng)沒有多大問題了。至此關(guān)于Python裝飾器總結(jié)完畢,有大神路過的時候,瞅一眼,有什么問題請批評指正!

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊舉報
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
裝飾器_裝飾器flage = true
原來Python裝飾器就是這么個東西
12步輕松搞定python裝飾器
《源碼探秘 CPython》64. 裝飾器是怎么實(shí)現(xiàn)的?
【連載電子書五】Python函數(shù)編程(下)
Python裝飾器的實(shí)現(xiàn)原理
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服