獲取更多文章和更新,請關(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ù)選擇。