廖雪峰Python3教程疑難理解(3):裝飾器

獲取更多文章和更新,請關(guān)注我的個人主頁:https://leiting6.cn

1 對函數(shù)添加裝飾器的理解

以廖老師這一節(jié)教程后面多層嵌套的代碼做例子,中間添加了一些print打印信息幫助理解代碼走向,最后today()調(diào)用的地方先注釋起來

def logger(text):
    print("--- 進入logger() ---")
    def decorator(func):
        print("--- 進入decorator() ---")
        def wrapper(*args, **kw):
            print("--- 進入wrapper() ---")
            print('%s %s()' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator

@logger('下面執(zhí)行: ')
def today():
    print("--- 進入today() ---")
    print('>> 2015-3-25')

print("today()函數(shù)的名字是: " + today.__name__)
#today()

保存上述代碼為.py文件并在命令行中執(zhí)行它,可以看到結(jié)果為:

D:\Python\test>python 7.py
--- 進入logger() ---
--- 進入decorator() ---
today()函數(shù)的名字是: wrapper

稍作分析一下:

  • 裝飾器@logger放在today()定義處,即相當于執(zhí)行了:
today = logger('下面執(zhí)行: ')(today)
  • 首先執(zhí)行(但不完全等價于執(zhí)行)
logger('下面執(zhí)行: ')

對照代碼和打印信息,可以看到,程序進入logger()函數(shù),傳入字符串"下面執(zhí)行: ",并在函數(shù)末尾返回decorator()函數(shù)。

  • 然后執(zhí)行(但不完全等價于執(zhí)行)
today = decorator(today)

依舊對照代碼和打印信息可以看到,程序進入decorator()函數(shù),傳入上面的字符串以及today()函數(shù)共同作為參數(shù),最后返回wrapper()函數(shù)賦值給today,此時,today函數(shù)已經(jīng)改變,因為today的name屬性值已經(jīng)變成了wrapper了。

接下來去掉代碼最后一樣的注釋,執(zhí)行一下,結(jié)果為:

D:\Python\test>python 7.py
--- 進入logger() ---
--- 進入decorator() ---
today()函數(shù)的名字是: wrapper
--- 進入wrapper() ---
下面執(zhí)行:  today()
--- 進入today() ---
>> 2015-3-25

再來分析一下...

  • 之前學習過返回函數(shù)我們知道,只將wrapper()函數(shù)賦值到today,wrapper()函數(shù)是不會執(zhí)行的,只有調(diào)用了賦值后的today()函數(shù),它才會執(zhí)行;

  • 比起加了注釋,這一次更進一步,程序進入到wrapper()函數(shù),先執(zhí)行wrapper()函數(shù)本身的內(nèi)容,就是現(xiàn)實下面即將執(zhí)行的函數(shù)的名字,需要注意的是,這里直接調(diào)用形參func的.name屬性值,而func還是最初傳入的today()函數(shù),所以它的函數(shù)名還是today;

  • 執(zhí)行完了wrapper()函數(shù)本身的功能之后,再執(zhí)行原始函數(shù),即today()函數(shù)(看了很多遍廖老師的教程,其實也沒找到代碼中有調(diào)用原始函數(shù)的痕跡,暫且這么理解了,應(yīng)該沒錯,當然這也是教程中的原話),打印出當前的日期,程序結(jié)束。

2 name屬性改變

從打印信息看得出來,today()函數(shù)添加裝飾器之后,其name屬性已經(jīng)發(fā)生變化,從today變成了wrapper,怎么讓他變回去呢?其實也很簡單,手動改變一下重新賦值就好,這里需要引入functools模塊。稍改一下代碼:

import functools

def logger(text):
    print("--- 進入logger() ---")
    def decorator(func):
        print("--- 進入decorator() ---")
        @functools.wraps(func)
        def wrapper(*args, **kw):
            print("--- 進入wrapper() ---")
            print('%s %s()' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator

@logger('下面執(zhí)行: ')
def today():
    print("--- 進入today() ---")
    print('>> 2015-3-25')

print("today()函數(shù)的名字是: " + today.__name__)
today()

執(zhí)行之后看結(jié)果:

D:\Python\test>python 7.py
--- 進入logger() ---
--- 進入decorator() ---
today()函數(shù)的名字是: today
--- 進入wrapper() ---
下面執(zhí)行:  today()
--- 進入today() ---
>> 2015-3-25

today()函數(shù)的名字已經(jīng)不會改變了,這樣如果程序有其他地方需要依賴其函數(shù)簽名來做其他事情的時候就不會出錯。其實wraps本身也是一個裝飾器,這里在wrapper()函數(shù)定義時調(diào)用,目的是將原函數(shù)對象的指定屬性復制給包裝函數(shù)對象, 默認有module,name,doc,或者通過參數(shù)選擇。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容