首先明確函數也是對象(python萬物皆對象),而且函數對象可以被賦值給變量,所以,通過變量也能調用該函數。
例如
>>> def now(): ... print ("2013-12-25") ... >>> f = now #函數名可以賦給一個變量,根據變量+()就可以調用這個函數 >>> f() 2013-12-25
函數對象有一個__name__屬性,可以獲取函數的名字:
>>> now.__name__ 'now' >>> f.__name__ 'now'
現在,假設我們要擴展now()函數的功能,比如,在函數調用前后自動打印日志,但又不希望修改now()函數的定義,這種在代碼運行期間動態增加功能的方式,稱之為“裝飾器”(Decorator)。本質上,decorator就是一個返回函數的高階函數
創建函數修飾符的規則:
(1)修飾符是一個函數
(2)修飾符取被修飾函數為參數
(3)修飾符返回一個新函數
(4)修飾符維護被維護函數的簽名
def log(func): def wrapper(*args, **kw): print ( "call %s()" % func.__name__) return func(*args, **kw) return wrapper @log #相當於執行了語句 now = log(now)@修飾符的功能就是把now當做參數傳遞給log,然后把log的返回值(wrapper)賦給now變量def now(): print ( "2013-12-25 ") >>> now() call now() 2013-12-25
這里的log()是一個decorator,是返回一個函數的高階函數;原來的now()函數仍然存在,只是現在同名的now變量指向了新的函數,於是調用now()將執行新函數,即wrapper()函數。
wrapper()函數的參數定義是(*args, **kw),因此,wrapper()函數可以接受任意參數的調用(如果被修飾函數now不接受參數,wrapper可以無參);在wrapper()函數內,首先打印日志,再緊接着調用原始函數。
此時經過decorator裝飾之后的函數,它們的__name__已經從原來的'now'變成了'wrapper':
>>> now.__name__ 'wrapper'
如果decorator本身需要傳入參數,那就需要編寫一個返回decorator的高階函數,多一層包裝。比如,要自定義log的文本:
2 def decorator(func): 3 def wrapper(*args , **kw): 4 print("%s %s()" % (text , func.__name__)) 5 return func(*args , **kw) 6 return wrapper 7 return decorator 8 9 @log('execute') #等價於now = log('execute')(now) log()返回一個函數名 10 def now(): 11 print("2013-12-25 ") 12 13 14 >>> now() 15 execute now() 16 2013-12-25
首先執行log('execute'),返回的是decorator函數,再調用返回的函數,參數是now函數,返回值最終是wrapper函數
以上兩種decorator的定義都沒有問題,但還差最后一步。因為函數也是對象,它有__name__等屬性,但你去看經過decorator裝飾之后的函數,它們的__name__已經從原來的'now'變成了'wrapper':
>>> now.__name__ 'wrapper'
所以,需要把原始函數的__name__等屬性復制到wrapper()函數中,否則,有些依賴函數簽名的代碼執行就會出錯。
不需要編寫wrapper.__name__ = func.__name__這樣的代碼,Python內置的functools.wraps就是干這個事的,所以,一個完整的decorator的寫法如下:
import functools #導入functools模塊def log(func): @functools.wraps(func) #py內置的functools.wraps 的修改函數屬性的方法wrapsdef wrapper(*args, **kw): print ('call %s()' % func.__name__) return func(*args, **kw) return wrapper
或者帶參數的decorator:
import functools def log(text): def decorator(func): @functools.wraps(func) def wrapper(*args, **kw): print '%s %s():' % (text, func.__name__) return func(*args, **kw) return wrapper return decorator
》》只需記住在定義wrapper()的前面加上@functools.wraps(func)即可。
functools模塊提供的修改函數屬性的方法wraps 進一步理解
def log(func): def wrapper(): print('log開始 ...') func() print('log結束 ...') return wrapper @log def test1(): print('test1 ..') def test2(): print('test2 ..') print(test1.__name__) print(test2.__name__) 運行結果: wrapper #test1.__name__改變了 test2
test1的函數名稱變了,如果某些代碼用到就會出問題,可以使用functools模塊提供的修改函數屬性的方法wraps,例如
from functools import wraps #可以寫成import functools 后面改@functools.wraps(func) 發揮命名空間的意義 def log(func): @wraps(func) def wrapper(): print('log開始 ...') func() print('log結束 ...') return wrapper @log def test1(): print('test1 ..') def test2(): print('test2 ..') print(test1.__name__) print(test2.__name__) 運行結果: test1 test2
更多請看 https://www.cnblogs.com/gdjlc/p/11182441.html 條理更加清晰!!!
》》總結:
--Python的decorator可以用函數實現,也可以用類實現,decorator可以增強函數的功能,定義起來雖然有點復雜,但使用起來非常靈活和方便。
--函數修飾符@的作用是為現有函數增加額外的功能,常用於插入日志、性能測試、事務處理等等