無論專案中還是面試都離不開裝飾器話題,裝飾器的強大在於它能夠在不修改原有業務邏輯的情況下對**進行擴充套件,許可權校驗、使用者認證、日誌記錄、效能測試、事務處理、快取等都是裝飾器的絕佳應用場景,它能夠最大程度地對**進行復用。
但為什麼初學者對裝飾器的理解如此困難,我認為本質上是對python函式理解不到位,因為裝飾器本質上還是函式
理解裝飾器前,需要明白函式的工作原理,我們先從乙個最簡單函式定義開始:
上面定義了乙個函式,名字叫def foo(num):
return num + 1
foo
,也可以把foo
可理解為變數名,該變數指向乙個函式物件
呼叫函式只需要給函式名加上括號並傳遞必要的引數(如果函式定義的時候有引數的話)
變數名value = foo(3)
print(value) # 4
foo
現在指向
函式物件,但它也可以指向另外乙個函式。
在python中,一切皆為物件,函式也不例外,它可以像整數一樣作為其它函式的返回值,例如:
呼叫函式 bar() 的返回值是乙個函式物件 ,因為返回值是函式,所以我們可以繼續對返回值進行呼叫(記住:呼叫函式就是在函式名後面加def foo():
return 1
def bar():
return foo
print(bar()) # print(bar()()) # 1
# 等價於
print(foo()) # 1
()
)呼叫bar()()
相當於呼叫foo()
,因為 變數 foo 指向的物件與 bar() 的返回值是同乙個物件。
函式還可以像整數一樣作為函式的引數,例如:
函式def foo(num):
return num + 1
def bar(fun):
return fun(3)
value = bar(foo)
print(value) # 4
bar
接收乙個引數,這個引數是乙個可被呼叫的函式物件,把函式foo
傳遞到bar
中去時,foo 和 fun 兩個變數名指向的都是同乙個函式物件,所以呼叫 fun(3) 相當於呼叫 foo(3)。
函式不僅可以作為引數和返回值,函式還可以定義在另乙個函式中,作為巢狀函式存在,例如:
def outer():
x = 1
def inner():
print(x)
inner()
outer() # 1
inner
做為巢狀函式,它可以訪問外部函式的變數,呼叫 outer 函式時,發生了3件事:
給 變數x
賦值為1
定義巢狀函式inner
,此時並不會執行 inner 中的**,因為該函式還沒被呼叫,直到第3步
呼叫 inner 函式,執行 inner 中的**邏輯。
再來看乙個例子:
同樣是巢狀函式,只是稍改動一下,把區域性變數 x 作為引數了傳遞進來,巢狀函式不再直接在函式裡被呼叫,而是作為返回值返回,這裡的 closure就是乙個閉包,本質上它還是函式,閉包是引用了自由變數(x)的函式(inner)。def outer(x):
def inner():
print(x)
return inner
closure = outer(1)
closure() # 1
繼續往下看:
上面這個函式這可能是史上最簡單的業務**了,雖然沒什麼用,但是能說明問題就行。現在,有乙個新的需求,需要在執行該函式時加上日誌:def foo():
print("foo")
功能實現,唯一的問題就是它需要侵入到原來的**裡面,把日誌邏輯加上去,如果還有好幾十個這樣的函式要加日誌,也必須這樣做,顯然,這樣的**一點都不pythonic。那麼有沒有可能在不修改業務**的提前下,實現日誌功能呢?答案就是裝飾器。def foo():
print("記錄日誌開始")
print("foo")
print("記錄日誌結束")
我沒有修改 foo 函式裡面的任何邏輯,只是給 foo 變數重新賦值了,指向了乙個新的函式物件。最後呼叫 foo(),不僅能列印日誌,業務邏輯也執行完了。現在來分析一下它的執行流程。def outer(func):
def inner():
print("記錄日誌開始")
func() # 業務函式
print("記錄日誌結束")
return inner
def foo():
print("foo")
foo = outer(foo)
foo()
這裡的 outer 函式其實就是乙個裝飾器,裝飾器是乙個帶有函式作為引數並返回乙個新函式的閉包,本質上裝飾器也是函式。outer 函式的返回值是 inner 函式,在 inner 函式中,除了執行日誌操作,還有業務**,該函式重新賦值給 foo 變數後,呼叫 foo() 就相當於呼叫 inner()
foo 重新賦值前:
重新賦值後,foo = outer(foo)
另外,python為裝飾器提供了語法糖@,它用在函式的定義處:
這樣就省去了手動給@outer
def foo():
print("foo")
foo()
foo
重新賦值的步驟。 python裝飾器理解 python裝飾器理解
裝飾器 在不改變原函式的 和呼叫方法的基礎上,給原函式增加額外的功能 理解宣告 為了方便理解,以下例子採用最簡潔的函式和新增的功能 給原函式新增乙個執行時間 import time def timer func def inner func return inner timer func timer...
python裝飾器 理解Python裝飾器
在python中,對於乙個函式,若想在其執行前後做點什麼,那麼裝飾器是再好不過的選擇,話不多說,上 usr bin env coding utf 8 script 01.py author howie from functools import wraps def decorator func wr...
python裝飾器理解 python裝飾器的理解
python裝飾器應該算是面試常考到的用點,之前在flask的應用中也是會常常用到,抽空仔細看書查資料理解了下裝飾器的概念,通過自己的理解記憶,應該對這個概念會有乙個大致上具體的了解。閉包說起python裝飾器,我們應該不得不談談閉包的概念。我對閉包的理解是,當函式存在巢狀,子函式呼叫了父函式的變數...