Python裝飾器為什麼難理解?

2021-08-08 18:19:15 字數 3558 閱讀 4568

無論專案中還是面試都離不開裝飾器話題,裝飾器的強大在於它能夠在不修改原有業務邏輯的情況下對**進行擴充套件,許可權校驗、使用者認證、日誌記錄、效能測試、事務處理、快取等都是裝飾器的絕佳應用場景,它能夠最大程度地對**進行復用。

但為什麼初學者對裝飾器的理解如此困難,我認為本質上是對python函式理解不到位,因為裝飾器本質上還是函式

理解裝飾器前,需要明白函式的工作原理,我們先從乙個最簡單函式定義開始:

def foo(num):

return num + 1

上面定義了乙個函式,名字叫foo,也可以把foo可理解為變數名,該變數指向乙個函式物件

呼叫函式只需要給函式名加上括號並傳遞必要的引數(如果函式定義的時候有引數的話)

value = foo(3)

print(value) # 4

變數名foo現在指向函式物件,但它也可以指向另外乙個函式。

在python中,一切皆為物件,函式也不例外,它可以像整數一樣作為其它函式的返回值,例如:

def foo():

return 1

def bar():

return foo

print(bar()) # print(bar()()) # 1

# 等價於

print(foo()) # 1

呼叫函式 bar() 的返回值是乙個函式物件 ,因為返回值是函式,所以我們可以繼續對返回值進行呼叫(記住:呼叫函式就是在函式名後面加())呼叫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 中的**邏輯。

再來看乙個例子:

def outer(x):

def inner():

print(x)

return inner

closure = outer(1)

closure() # 1

同樣是巢狀函式,只是稍改動一下,把區域性變數 x 作為引數了傳遞進來,巢狀函式不再直接在函式裡被呼叫,而是作為返回值返回,這裡的 closure就是乙個閉包,本質上它還是函式,閉包是引用了自由變數(x)的函式(inner)。

繼續往下看:

def foo():

print("foo")

上面這個函式這可能是史上最簡單的業務**了,雖然沒什麼用,但是能說明問題就行。現在,有乙個新的需求,需要在執行該函式時加上日誌:

def foo():

print("記錄日誌開始")

print("foo")

print("記錄日誌結束")

功能實現,唯一的問題就是它需要侵入到原來的**裡面,把日誌邏輯加上去,如果還有好幾十個這樣的函式要加日誌,也必須這樣做,顯然,這樣的**一點都不pythonic。那麼有沒有可能在不修改業務**的提前下,實現日誌功能呢?答案就是裝飾器。

def outer(func):

def inner():

print("記錄日誌開始")

func() # 業務函式

print("記錄日誌結束")

return inner

def foo():

print("foo")

foo = outer(foo)

foo()

我沒有修改 foo 函式裡面的任何邏輯,只是給 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裝飾器,我們應該不得不談談閉包的概念。我對閉包的理解是,當函式存在巢狀,子函式呼叫了父函式的變數...