自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Python裝飾器(Decorator)不過如此,是我想多了

開發(fā) 后端
Python裝飾器是Python中一個非常有趣的特性,可以利用Python裝飾器對一個函數(shù)包裝再包裝,其實從效果上看有一點像AOP中的切面,也就是對函數(shù)調(diào)用進(jìn)行攔截,那么通過Python裝飾器可以做哪些有趣的事情,以及Python裝飾器的原理是什么呢?繼續(xù)看本文吧!

[[382099]]

 1. 疊加使用Python裝飾器

最近有學(xué)員問,Python中也有與Java類似的@xxxx語法,這到底是什么意思呢?現(xiàn)在我就來回答這個問題。

Java中的@xxxx語法是注解(Annotation),而Python中的@xxxx語法是裝飾器(decorator),盡管在語法上類似,但作用完全不同。Java的注解相當(dāng)于語法元素(方法、類、接口等)的元數(shù)據(jù)。而Python的裝飾器是對Python函數(shù)(方法)的包裝,現(xiàn)在我們來舉個例子。

  1. @makebold 
  2. @makeitalic 
  3. def say(): 
  4.    return "Hello" 
  5. print(say())) 

這段代碼,對函數(shù)say使用了2個裝飾器:@makebold和@makeitalic,而且是疊加狀態(tài)。@makeitalic會首先作用于say函數(shù),然后@makebold會作用于@makeitalic裝飾器的結(jié)果,這兩個裝飾器分別用......包裝say函數(shù)返回的字符串,所以這段代碼的執(zhí)行結(jié)果如下:

Hello

不過直接執(zhí)行這段代碼肯定會出錯的,這是因為這兩個裝飾器還沒定義,下面就看下如何定義這兩個裝飾器。

2. 定義Python裝飾器

裝飾器本身就是一個普通的Python函數(shù),只是函數(shù)的參數(shù)需要是函數(shù)類型(通常傳入被裝飾的函數(shù)),定義形式如下:

  1. <b><i>Hello</i></b> 

現(xiàn)在就來定義前面給出的兩個裝飾器:

  1. from functools import wraps 
  2.  
  3. def makebold(fn): 
  4.     @wraps(fn) 
  5.     def makebold_wrapped(*args, **kwargs): 
  6.         return "<b>" + fn(*args, **kwargs) + "</b>" 
  7.     return makebold_wrapped 
  8.  
  9. def makeitalic(fn): 
  10.     @wraps(fn) 
  11.     def makeitalic_wrapped(*args, **kwargs): 
  12.         return "<i>" + fn(*args, **kwargs) + "</i>" 
  13.     return makeitalic_wrapped 

很明顯,makebold和makeitalic是兩個普通的Python函數(shù),而且在函數(shù)內(nèi)部分別定義了另外兩個函數(shù),而且這兩個函數(shù)被作為返回值返回。這其中使用了wraps函數(shù),這個函數(shù)其實可以不加,不過會有一些副作用。

由于使用@makebold和@makeitalic修飾某個函數(shù)時,會將這個被修飾的函數(shù)傳入makebold函數(shù)和makeitalic函數(shù),也就是說,fn參數(shù)就是這個被修飾的函數(shù)。而在外部調(diào)用這個被修飾函數(shù)時,實際上是調(diào)用了修飾器返回的函數(shù),也就是makebold_wrapped和makeitalic_wrapped,這樣就會導(dǎo)致被修飾函數(shù)屬性的改變,如函數(shù)名、函數(shù)文檔等,現(xiàn)在可以先去掉@wraps,執(zhí)行下面的代碼:

  1. @makeitalic 
  2. @makebold 
  3. def say(): 
  4.    return "Hello" 
  5. print(say.__name__)   # 輸出函數(shù)名 

會輸出如下的內(nèi)容:

  1. makebold_wrapped 

由于最后使用了@makebold裝飾器,所以輸出的是makebold函數(shù)返回的makebold_wrapped函數(shù)的名字。如果加上@wraps,那么就會輸出say。

要注意,需要通過裝飾器方式調(diào)用wraps函數(shù),這樣其實就相當(dāng)于在@makebold外面又包了一層裝飾器(wraps)。

3. 理解Python函數(shù)

現(xiàn)在我們已經(jīng)了解了如何自定義Python裝飾器,但應(yīng)該如何理解裝飾器呢?到底是什么原理呢?要想理解Python裝飾器,首先應(yīng)該知道Python函數(shù)就是對象,看下面的例子:

  1. def shout(word="yes"): 
  2.     return word.capitalize() 
  3. # 輸出:Yes 
  4. print(shout()) 
  5. # 將shout函數(shù)賦給另一個變量,這里并沒有使用圓括號, 
  6. # 所以不是調(diào)用函數(shù),而是將函數(shù)賦給另一個變量,也就是為函數(shù)起一個別名 
  7. scream = shout 
  8.  
  9. # 可以用scream調(diào)用shout函數(shù) 
  10. # 輸出:Yes 
  11. print(scream()) 
  12.  
  13. # 目前,同一個函數(shù),有兩個引用:scream和shout,可以使用del刪除一個引用 
  14. del shout 
  15. try: 
  16.     # 該引用刪除后,就不能通過該引用調(diào)用函數(shù)了 
  17.     print(shout()) 
  18. except NameError as e: 
  19.     print(e) 
  20.  
  21. # 仍然可以通過另外一個引用調(diào)用函數(shù) 
  22. # 輸出:Yes 
  23. print(scream()) 

這段代碼演示了把函數(shù)作為對象使用。如果加一對圓括號,就是調(diào)用函數(shù),如果不加一對圓括號,函數(shù)就是對象,可以賦給另一個變量,也可以作為函數(shù)參數(shù)值傳入函數(shù)。

由于Python函數(shù)本身是對象,所以可以在任何地方定義,包括函數(shù)內(nèi)容,這就是Python內(nèi)建函數(shù),代碼如下:

  1. def talk(): 
  2.     # 內(nèi)嵌函數(shù) 
  3.     def whisper(word="YES"): 
  4.         return word.lower()+"..." 
  5.  
  6.     # 調(diào)用內(nèi)嵌函數(shù) 
  7.     print(whisper()) 
  8.  
  9. # 調(diào)用talk,whisper函數(shù)在talk內(nèi)部被調(diào)用 
  10. # 輸出:yes... 
  11. talk() 
  12.  
  13. try: 
  14.     # 但whisper函數(shù)在talk函數(shù)外部并不可見,所以調(diào)用會哦拋出異常 
  15.     print(whisper()) 
  16. except NameError as e: 
  17.     print(e) 

現(xiàn)在來總結(jié)下,Python函數(shù)的特性如下:

(1)可以將函數(shù)本身賦給一個變量,或作為參數(shù)值傳入函數(shù)(方法);

(2)可以在一個函數(shù)(方法)內(nèi)部定義;

有了這兩個特性,就意味著函數(shù)可以被另一個函數(shù)返回,看下面的代碼:

  1. def getTalk(kind="shout"): 
  2.  
  3.     #  定義第1個內(nèi)嵌函數(shù) 
  4.     def shout(word="yes"): 
  5.         return word.capitalize()+"!" 
  6.     # 定義第2個內(nèi)嵌函數(shù) 
  7.     def whisper(word="yes") : 
  8.         return word.lower()+"..." 
  9.  
  10.     # 根據(jù)參數(shù)值返回特定的函數(shù) 
  11.     if kind == "shout"
  12.         # 這里沒有使用一對圓括號,所以不是調(diào)用函數(shù),而是返回函數(shù)本身 
  13.         return shout 
  14.     else
  15.         return whisper 
  16.  
  17.  
  18. # talk是函數(shù)本身,并沒有被調(diào)用 
  19. talk = getTalk() 
  20.  
  21. # 輸出函數(shù)本身 
  22. # 輸出:<function getTalk.<locals>.shout at 0x7f93a00475e0> 
  23. print(talk) 
  24.  
  25. # 調(diào)用talk函數(shù)(其實是shout函數(shù)) 
  26. print(talk()) 
  27. #outputs : Yes! 
  28.  
  29. # 調(diào)用whisper函數(shù) 
  30. print(getTalk("whisper")()) 

在這段代碼中,getTalk函數(shù)根據(jù)kind參數(shù)的值返回不同的內(nèi)嵌函數(shù),所以getTalk函數(shù)的返回值是函數(shù)本身,或稱為函數(shù)對象,如果要調(diào)用函數(shù),需要使用一對圓括號,如getTalk()()。

根據(jù)這一特性,我們還可以做更多事,例如,在調(diào)用一個函數(shù)之前自動完成其他工作,看下面的代碼:

  1. def doSomethingBefore(func): 
  2.     print("I do something before then I call the function you gave me"
  3.     print(func()) 
  4.  
  5. doSomethingBefore(talk) 

其實這段代碼用doSomethingBefore函數(shù)包裝了talk,這樣可以通過doSomethingBefore函數(shù)調(diào)用talk函數(shù),并在調(diào)用talk函數(shù)之前輸出一行文本。

4. Python裝飾器的原理

理解了Python函數(shù),再理解Python裝飾器就容易得多了。廢話少說,先看下面的代碼:

  1. # 裝飾器函數(shù),參數(shù)是另一個函數(shù)(被裝飾的函數(shù)) 
  2. def my_shiny_new_decorator(a_function_to_decorate): 
  3.     # 裝飾器的內(nèi)嵌函數(shù),用來包裝被修飾的函數(shù) 
  4.     def the_wrapper_around_the_original_function(): 
  5.         # 在調(diào)用被修飾函數(shù)之前輸出一行文本 
  6.         print("Before the function runs"
  7.  
  8.         # 調(diào)用被裝飾函數(shù) 
  9.         a_function_to_decorate() 
  10.  
  11.         # 在調(diào)用被修飾函數(shù)之后輸出一行文本 
  12.         print("After the function runs"
  13.  
  14.     # 返回包裝函數(shù) 
  15.     return the_wrapper_around_the_original_function 
  16.  
  17. # 這個函數(shù)將被my_shiny_new_decorator函數(shù)修飾 
  18. def a_stand_alone_function(): 
  19.     print("I am a stand alone function, don't you dare modify me"
  20.  
  21. # 調(diào)用函數(shù) 
  22. a_stand_alone_function() 
  23.  
  24. # 修飾a_stand_alone_function函數(shù) 
  25. a_stand_alone_function_decorated = my_shiny_new_decorator(a_stand_alone_function) 
  26. a_stand_alone_function_decorated() 

執(zhí)行這段代碼,會輸出如下內(nèi)容:

  1. I am a stand alone function, don't you dare modify me 
  2. Before the function runs 
  3. I am a stand alone function, don't you dare modify me 
  4. After the function runs 

在這段代碼中,通過my_shiny_new_decorator函數(shù)修飾了a_stand_alone_function函數(shù),并在調(diào)用a_stand_alone_function函數(shù)前后各輸出了一行文本。其實這就是Python裝飾器的作用:包裝函數(shù)。只是這里并沒有使用裝飾器的語法,而是用了最樸素的方式直接調(diào)用了裝飾器函數(shù)來修飾a_stand_alone_function函數(shù)。

如果用裝飾器來修飾a_stand_alone_function函數(shù),那么可以用下面的代碼。

  1. @my_shiny_new_decorator 
  2. def a_stand_alone_function(): 
  3.     print("I am a stand alone function, don't you dare modify me"

這時再調(diào)用a_stand_alone_function函數(shù),就會自動使用my_shiny_new_decorator函數(shù)對a_stand_alone_function函數(shù)進(jìn)行包裝,也就是說,@my_shiny_new_decorator是my_shiny_new_decorator(a_stand_alone_function)的簡寫形式。

本文轉(zhuǎn)載自微信公眾號「極客起源」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系極客起源公眾號。

 

責(zé)任編輯:武曉燕 來源: 極客起源
相關(guān)推薦

2018-12-29 16:40:29

c語言編程語言指針

2021-05-11 09:27:54

裝飾器模式代碼開發(fā)

2020-05-21 08:24:17

阿里SQL查詢

2020-08-04 11:35:38

Vue前端裝飾器

2024-05-15 09:13:37

GPT-4oAI

2017-07-27 20:50:55

PythonDecorator裝飾器

2023-02-07 07:47:52

Python裝飾器函數(shù)

2021-06-17 09:32:17

前端TypeScript 技術(shù)熱點

2010-02-01 17:50:32

Python裝飾器

2022-09-19 23:04:08

Python裝飾器語言

2016-11-01 09:24:38

Python裝飾器

2010-12-29 11:39:29

老板

2023-12-11 15:51:00

Python裝飾器代碼

2024-05-24 11:36:28

Python裝飾器

2025-01-22 15:58:46

2021-06-01 07:19:58

Python函數(shù)裝飾器

2012-07-06 09:27:29

jQuery2.0

2018-05-21 09:30:04

操作系統(tǒng)Linux資源

2024-10-16 15:16:37

Python裝飾器開發(fā)

2023-12-13 13:28:16

裝飾器模式Python設(shè)計模式
點贊
收藏

51CTO技術(shù)棧公眾號