python 自定義裝飾器例項詳解

2022-10-04 16:48:16 字數 3207 閱讀 8293

先看乙個例子

def deco(func):

print("before myfunc() called.")

func()

print("after myfunc() called.")

return func

@deco

def myfunc():

print("myfunc() called.")

# myfunc = deco(myfunc) # 與上面的@deco等價

myfunc()

print("***********")

myfunc()

會發現,輸出為

before myfunc() called.

myfunc() called.

after myfunc() tacpsruecalled.

myfunc() called.

***********

myfunc() called.

這就是說,裝飾器裡面的東西只呼叫了一次,為什麼呢?

是因為,在myfunc()函式的定義前面加一句@deco,本質上完全等價於在出現def myfunc()後,先將下面所有內容的首位址傳遞給func,然後緊接著加上一句 myfunc = deco(myfunc)。執行這句話,表示func代表了本來定義的myfunc()的函式體,同時函式myfunc()的位址傳遞給deco()函式,即 myfunc -> func,這裡就相當於myfunc的值與func的值完全相同了。然後執行裝飾器裡面的內容,最後返回給func,傳遞給myfunc。接下來在呼叫myfunc()的時候,列印輸出「myfunc() called」。第二次呼叫myfunc()函式的時候,依然只列印輸出「myfunc() called」。為什麼第二次沒有執行裝飾器裡面的內容呢?是因為,myfunc = deco(myfunc)這句話只執行了一次,而這句話,才是真正執行裝飾器裡面的內容的話。

上面的**表示,裝飾器相當於只對第一次呼叫他的函式進行了裝飾,那麼,怎麼對每次呼叫的函式都裝飾呢?接著看

def deco(func):

def wrapper(*args, **kwargs): # *args, **kwargs用於接收func的引數

print("before myfunc() called.")

func(*args, **kwargs)

print("after myfunc() called.")

return wrapper

@deco

def myfunc(a, b):

print(a+b)

# myfunc = deco(myfunc) # 與上面的@deco等價

myfunc(1, 2)

print("***********")

myfunc(3, 4)

該**輸出結果為

before myfunc() called.

3after myfunc() called.

***********

before myfunc() called.

7after myfunc() called.

我們說了,在myfunc()函式的定義前面加一句@deco,本質上完全等價於在出現def?myfunc()後,先將下面所有內容的首位址傳遞給func,然後緊接著加上一句 myfunc = deco(myfunc)。執行myfunc(1, 2)命令的時候,myfunc函式體的位址早已經傳遞給了deco()函式,返回的是wrapper。這是myfunc所代表的位址不再是原來的myfunc的位址,而是wrapper函式的位址。所以,以後凡是出現myfunc()的地方,都是在呼叫wrapper()函式。即myfunc(1, 2)就是wrapper(1, 2),所以每次呼叫myfunc()時候,裝飾器裡面的內容都會被執行了。而wrapper()函式體裡面的func,就代表了原來myfunc()的函式體。

怎麼進一步理解「在出現def?myfunc()後,先將下面所有內容的首位址傳遞給func」這句話呢?看:

def deco(func):

def wrapper(*args, **kwargs): # *args, **kwargs用於接收func的引數

print("wrapper的位址:", wrapper)

func(*args, **kwargs)

print("func的位址:", func)

return wrapper

@deco

def myfunc(a, b):

print("myfunc的位址:",myfunc)

print(a+b)

# myfunc = deco(myfunc) # 與上面的@deco等價

myfunc(1, 2)

print("***********")

print("修改後myfunc的位址:",myfunc)

執行結果:

wrapper的位址: .wrapper at 0x0000023aa9ff58c8>

myfunc的位址: .wrapper at 0x0000023aa9ff58c8>

3func的位址:

***********

修改後myfunc的位址: .wrapper at 0x0000023aa9ff58c8>

程式執行到myfunc(1,2)的時候,本質上是在執行wrapper(1, 2),於是先輸出wrapper的位址,再執行func()函式。執行func()函式的時候,輸出myfunc()的位址,(可見,此時myfunc的值與wrapper的是相等),再列印3。當輸出func()函式的位址,可見func()函式的位址與myfunc()函式的位址不一樣了!!!!這www.cppcns.com就是說,原來定義的myfunc()函式的函式體,已經屬於func了,而不屬於myfunc了!!

進一步見證奇蹟!!

def deco(func):

def wrapper(*args, **kwargs): # *args, *程式設計客棧*kwargs用於接收func的引數

pass

return wrapper

@deco

def myfunc(a, b):

print(a+b)

myfunc(1, 2)

該**沒有任何輸出。那是因為,執行myfunc(1, 2)的時候,本程式設計客棧質上是執行w 2)。而wrapper(1, 2)又不幹任何事情,所以沒有輸出。至於print(a+b)這句話,他的位址已經屬於func了。

帶引數的裝飾器,可以參見其他文章

python 自定義裝飾器 詳解

先看乙個例子 def deco func print before myfunc called.func print after myfunc called.return func deco def myfunc print myfunc called.myfunc deco myfunc 與上面的...

antd自定義分頁器 自定義分頁器例項

def init self,current page num,all count,request,per page num 2,pager count 11 封裝分頁相關資料 param current page num 當前訪問頁的數字 param all count 分頁資料中的資料總條數 pa...

python自定義裝飾器的情況(詳解

關於函式定義的單一裝飾器 一般的通用裝飾器 多個裝飾器的使用 帶有引數的裝飾器 類裝飾器 這是裝飾器的自定義和內部機制一些初識 先強調裝飾器的要求便是 不改變原來的函式 裝飾器就乙個函式引用引數 不改變呼叫方式 我先介紹一些閉包,因為裝飾器也是基於閉包來實現的,區別閉包和裝飾器的區別就可以看外層函式...