需要理解的一些概念
要理解Python中的裝飾器,我覺得還是應該從最基本的概念開始:
裝飾器模式:所謂的裝飾器模式,可以簡單地理解為“在不改變原有內部實現的情況下,為函數或者類添加某種特性”。這樣我們就可以將一些與業務無關、具有通用性的代碼抽象出來,作為裝飾器附加到需要這些代碼的函數或者類之上。用面向切面編程的思想解釋就是“裝飾器應該是一個切面”。
函數是一等公民:意思就是函數可以被當成普通變量一樣使用。在Python中,可以把函數賦值給變量,可以將函數作為其它函數的參數,也可以將函數作為其它函數的返回值。
閉包:我們都知道局部作用域可以引用全局作用域中的變量,相似的,當一個函數內部又定義了其它函數的時候,內部函數可以使用外部函數所在作用域的變量,這就是閉包。
從最簡單的裝飾器做起
理解完以上的概念之后,我們嘗試一下利用這些特性實現一個簡單的裝飾器。
首先明確一下需求,我們有時候會需要在函數調用時打印一個相應的日志,雖然可以通過在所有需要打印日志的函數代碼中嵌入打印日志的代碼來實現,但這種方法不僅增加了許多重復代碼,而且在業務代碼中嵌入與業務無關的代碼增加了整體的耦合度。因此,我們需要實現一個裝飾器,這個裝飾器在函數調用時可以打印一個日志記錄函數調用行為。
如果我們有以下函數foo,代表具體的業務函數:
def foo():  print('in function foo')我們設想通過調用foo = deco(foo)實現給函數foo增加打印日志的功能,并且不影響它原有的業務。那么在這種設想下,裝飾器deco應該也是一個函數,它接收另一個函數作為輸入,并返回一個新的、經過裝飾的函數。在Python中,我們可以這么寫:
def deco(func): # 接收一個函數作為參數  def new_func():    print(f'[log] run function {func.__name__}') # 此處使用了Python3.6的格式化字符串    func() # 閉包,在內部函數中使用了外部函數的變量  return new_func # 將新函數作為返回值返回執行一下試試效果:
>>> foo = deco(foo)>>> foo()[log] run function fooin function foo
不錯,至此我們已經實現了一個最簡單的裝飾器!在上面的代碼中,裝飾器deco接收任意的函數作為參數,再在內部構造另一個函數,利用閉包的特性,可以在新函數里調用存在于裝飾器deco局部作用域中的函數func。
神奇的@
按照上面那么寫,每次我們都得為需要裝飾的函數賦一個新值,萬一函數或者裝飾器的數量增加了,手動寫賦值和函數調用就會變得非常麻煩。那么在Python中,有沒有更優雅的寫法呢?答案是有的,你只需要一個@符號。
新聞熱點
疑難解答