獲取更多文章和更新,請關注我的個人主頁:https://leiting6.cn
1 對函數添加裝飾器的理解
以廖老師這一節教程后面多層嵌套的代碼做例子,中間添加了一些print打印信息幫助理解代碼走向,最后today()調用的地方先注釋起來:
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('下面執行: ')
def today():
print("--- 進入today() ---")
print('>> 2015-3-25')
print("today()函數的名字是: " + today.__name__)
#today()
保存上述代碼為.py文件并在命令行中執行它,可以看到結果為:
D:\Python\test>python 7.py
--- 進入logger() ---
--- 進入decorator() ---
today()函數的名字是: wrapper
稍作分析一下:
- 裝飾器@logger放在today()定義處,即相當于執行了:
today = logger('下面執行: ')(today)
- 首先執行(但不完全等價于執行)
logger('下面執行: ')
對照代碼和打印信息,可以看到,程序進入logger()函數,傳入字符串"下面執行: ",并在函數末尾返回decorator()函數。
- 然后執行(但不完全等價于執行)
today = decorator(today)
依舊對照代碼和打印信息可以看到,程序進入decorator()函數,傳入上面的字符串以及today()函數共同作為參數,最后返回wrapper()函數賦值給today,此時,today函數已經改變,因為today的name屬性值已經變成了wrapper了。
接下來去掉代碼最后一樣的注釋,執行一下,結果為:
D:\Python\test>python 7.py
--- 進入logger() ---
--- 進入decorator() ---
today()函數的名字是: wrapper
--- 進入wrapper() ---
下面執行: today()
--- 進入today() ---
>> 2015-3-25
再來分析一下...
之前學習過返回函數我們知道,只將wrapper()函數賦值到today,wrapper()函數是不會執行的,只有調用了賦值后的today()函數,它才會執行;
比起加了注釋,這一次更進一步,程序進入到wrapper()函數,先執行wrapper()函數本身的內容,就是現實下面即將執行的函數的名字,需要注意的是,這里直接調用形參func的.name屬性值,而func還是最初傳入的today()函數,所以它的函數名還是today;
執行完了wrapper()函數本身的功能之后,再執行原始函數,即today()函數(看了很多遍廖老師的教程,其實也沒找到代碼中有調用原始函數的痕跡,暫且這么理解了,應該沒錯,當然這也是教程中的原話),打印出當前的日期,程序結束。
2 name屬性改變
從打印信息看得出來,today()函數添加裝飾器之后,其name屬性已經發生變化,從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('下面執行: ')
def today():
print("--- 進入today() ---")
print('>> 2015-3-25')
print("today()函數的名字是: " + today.__name__)
today()
執行之后看結果:
D:\Python\test>python 7.py
--- 進入logger() ---
--- 進入decorator() ---
today()函數的名字是: today
--- 進入wrapper() ---
下面執行: today()
--- 進入today() ---
>> 2015-3-25
today()函數的名字已經不會改變了,這樣如果程序有其他地方需要依賴其函數簽名來做其他事情的時候就不會出錯。其實wraps本身也是一個裝飾器,這里在wrapper()函數定義時調用,目的是將原函數對象的指定屬性復制給包裝函數對象, 默認有module,name,doc,或者通過參數選擇。