Python 裝飾器

1. 什麼是裝飾器?python

  顧名思義,裝飾器就是在方法上方標一個帶有@符號的方法名,以此來對被裝飾的方法進行點綴改造。shell

  當你明白什麼是裝飾器以後,天然會以爲這個名字取得恰如其分,但做爲初學者來講多少仍是會有些迷茫。下面用代碼來講明怎麼理解裝飾器。函數

#腳本1
def target():
    print(this is target)
    
def decorator(func):
    func()
    print(this is decorator)
    
decorator(target)
-------------------------------------------
    
運行結果爲:
    
this is target
this is decorator

Python容許將方法看成參數傳遞,所以以上腳本就是將target方法做爲參數傳入decorator方法中,這其實也是裝飾器的工做原理,以上代碼等同於:學習

#腳本2
def decorator(func):
    func()
    print(this is decorator)
    
@decorator
def target():
    print(this is target)
    
target
-------------------------------------------
運行結果:
    
this is target
this is decorator

所以能夠看出,所謂的裝飾器就是利用了Python的方法可做參數傳遞的特性,將方法target做爲參數傳遞到方法decorator中。this

@decorator
def target():
   ...對象

  這種在一個方法的上方加一個@符號的寫法,就是表示位於下方的方法將被做爲參數傳遞到位於@後面的decorator方法中。使用@符號只是讓腳本1中的代碼換了一個寫法,更加好看,固然也會更加靈活與實用,後面會講到這點。但它們的本質實際上是同樣的,這也就是裝飾器的工做原理。內存

  2. 裝飾器的原理字符串

  若是你仔細看的話,會在腳本2中發現一個問題,那就是腳本2中最後一行的target只是一個方法名字,它不是正確的方法調用,正確寫法應該加上左右括號的target(),以下:get

#腳本3
    
def decorator(func):
    func()
    print(this is decorator)
    
@decorator
def target():
    print(this is target)
    
target()
--------------------------------------------
運行結果:
    
this is target
this is decorator
Traceback (most recent call last):
  File "C:/Users/Me/Desktop/ff.py", line 34, in <module>
    target()
TypeError: NoneType object is not callable

正如你所看到的,若是按照正確的寫法,運行結果你會看到應該出現的兩行打印文字"this is target"和"this is decorator",還會出現錯誤提示,ff.py是我爲寫這篇心得臨時編寫的一個py腳本名字,提示說'NoneType'對象不可調用。這是怎麼回事?好吧,我如今必須告訴你,其實腳本2和腳本3中並非一個使用裝飾器的正確寫法,不是使用錯誤,而是做爲裝飾器的decorator方法寫的並不友好,是的,我不認爲它是錯誤的寫法,只是不友好。但只要你明白其中的道理,使用恰當的手段也是能夠運行正常的,這就是爲何腳本2看似寫錯了調用方法卻得出了正確的結果。固然學習仍是得規規矩矩,後面我會具體說正確的裝飾器怎麼書寫,在這裏我先解釋了一下腳本2和腳本3的運行原理,瞭解它們的運行原理和錯誤緣由,其實就是了解裝飾器的原理。編譯器

  腳本2和腳本3的區別在於target和target(),也就是說真正的差異在於()這個括號。當()被附加在方法或者類後面時,表示調用,或者稱爲運行及實例化,不管稱呼怎樣,本質意義沒有不一樣,都是調用給出的對象,當對象不具有調用性的時候,就會報錯:'某個類型' object is not callable。當一個方法被調用後,即target(),是否能被再次執行,取決於它是否會return一個對象,而且該對象能夠被調用。也許你會有點迷糊,對比一下代碼會比較容易理解我想表達的意思:

>>>def returnme():
>>>    print(this is returnme)
     
>>>def target():
>>>    print(this is target)
      
>>>target
<function target at 0x00000000030A40D0>
      
>>>target()
target
<function returnme at 0x00000000030A4268>
     
>>>target()()
target
returnme
    
>>>returnme()()
returnme
Traceback (most recent call last):
  File "<pyshell#15>", line 1, in <module>
    returnme()()
TypeError: NoneType object is not callable

如上所示,當直接在腳本中輸入target,它只是告訴編譯器(我想是編譯器吧,由於我也不是很懂所謂編譯器的部分),總之就是告訴那個不知道在哪一個角落控制着全部python代碼運行的「大腦」,在

0x00000000030A40D0位置(這個位置應該是指內存位置)存有一個function(方法)叫target;在target後面加上(),表示調用該方法,即輸入target(),「大腦」便按照target方法所寫的代碼逐條執行,因而打印出了target字符串,而且「大腦」明白在0x00000000030A4268位置有一個叫returnme的方法;由於target對象調用後是會返回一個returnme方法,而且方法是能夠被調用的,所以你能夠直接這樣書寫target()(),「大腦」會逐條執行target中的代碼,而後return一個returnme,由於多加了一個(),表示要對返回的returnme進行調用,因而再次逐條執行returnme中的代碼,最後便能看到1五、16的打印結果;而returnme方法是沒有返回任何可調用的對象,所以當輸入returnme()()時,「大腦」會報錯。

  下面咱們能夠來解釋一下腳本2和腳本3的運行詳情,以前說過,裝飾器的工做原理就是腳本1代碼所演示的那樣。

@decorator
def target():
    ...
    
等同於
    
def decorator(target)():
    ...
    
注:python語法中以上寫法是非法的,以上只是爲了便於理解。

當你調用被裝飾方法target時,其實首先被執行的是做爲裝飾器的decorator函數,而後「大腦」會把target方法做爲參數傳進去,因而:

#腳本2
def decorator(func):
    func()
    print(this is decorator)
      
@decorator
def target():
    print(this is target)
      
target
-------------------------------------------
    
實際運行狀況:
首先調用decorator方法:decorator()
由於decorator方法含1個參數,所以將target傳入:decorator(target)
運行代碼「func()」,根據傳入的參數,實際執行target(),結果打印出:this is target
運行代碼"print(this is decorator)",結果打印出:this is decorator

對比腳本3的運行狀況:

#腳本3
def decorator(func):
    func()
    print(this is decorator)
      
@decorator
def target():
    print(this is target)
      
target()
-------------------------------------------
    
實際運行狀況:
首先調用decorator方法:decorator()
由於decorator方法含1個參數,所以將target傳入:decorator(target)
運行代碼「func()」,根據傳入的參數,實際執行target(),結果打印出:this is target
運行代碼"print(this is decorator)",結果打印出:this is decorator
    
以上與腳本2中運行狀況徹底相同,接下來即是執行腳本2中target沒有的(),也就是執行調用命令。
因爲decorator(target)沒有返回一個能夠被調用的對象,所以「大腦」提示錯誤:NoneType object is not callable
相關文章
相關標籤/搜索