因爲函數也是一個對象,並且函數做爲一個特殊的對象能夠被賦值給其餘變量(value = pringname()),相反,經過變量也是能夠調用函數的,如下是一個簡單的例子:app
1 >>> def printName(): 2 ... print("My name is Jobs") 3 ... 4 >>> name = printName 5 >>> 6 >>> printName() 7 My name is Jobs 8 >>> name() 9 My name is Jobs
由以上代碼咱們不難看出,printName()函數被賦值給name變量,一樣用name變量也能夠調用printName()函數。此時他們是相同的,在這裏說一點小知識:函數對象有函數
一個__name__
屬性,能夠拿到函數的名字:spa
例子:日誌
1 >>> 2 >>> name.__name__ 3 'printName' 4 >>> printName.__name__ 5 'printName' 6 7
如今,假設咱們要加強printName函數的功能,好比,在函數調用先後自動打印日誌,但又不但願修改printName函數的定義,這種在代碼運行期間動態增長功能的方式,稱之爲「code
裝飾器」(Decorator)。本質上,decorator就是一個返回函數的高階函數。因此,咱們要定義一個能打印日誌的decorator,能夠定義以下:對象
例子一:blog
1 >>> 2 >>> def log(func): 3 ... def wrapper(*args,**kw): 4 ... print 'call %s()' %func.__name__ 5 ... return func(*args,**kw) 6 ... return wrapper 7 ...
觀察上面的log
,由於它是一個decorator,因此接受一個函數做爲參數,並返回一個函數。咱們要藉助Python的@語法,把decorator置於函數的定義處:class
1 >>> @log 2 ... def printName(): 3 ... print("My name is Jobs") 4 ... 5 >>> printName() 6 call printName() 7 My name is Jobs
例子二:import
1 >>> 2 >>> @log 3 ... def printAge(): 4 ... print '%s,my age is 120' %printName() 5 ... 6 >>> job = printAge() 7 call printAge() 8 call printName() 9 My name is Jobs 10 None,my age is 120
當你調用printName或者printAge()時,不只會調用函數自己,並且會調用在調用該函數以前打印一些日誌。把@log放在printAge()函數以前至關於執行了語句:變量
name= log(printName)
因爲log()
是一個decorator,返回一個函數,因此,原來的now()
函數仍然存在,只是如今同名的printName()了新的函數,因而調用printName()
將執行新函數,即在log()
函數中返回的wrapper()
函數。
wrapper()
函數的參數定義是(*args, **kw)
,所以,wrapper()
函數能夠接受任意參數的調用。在wrapper()
函數內,首先打印日誌,再緊接着調用原始函數。
若是decorator自己須要傳入參數,那就須要編寫一個返回decorator的高階函數,寫出來會更復雜。好比,要自定義log的文本:
1 def log(text): 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
這個3層嵌套
1 >>> def log(text): 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('excute') 10 ... def printName(): 11 ... print("My name is Jobs!") 12 ... 13 >>> printName() 14 excute printName(): 15 My name is Jobs! 16 >>>
咱們來剖析上面的語句,首先執行log('execute')
,返回的是decorator
函數,再調用返回的函數,參數是now
函數,返回值最終是wrapper
函數。
以上兩種decorator的定義都沒有問題,但還差最後一步。由於咱們講了函數也是對象,它有__name__
等屬性,但你去看通過decorator裝飾以後的函數,它們的__name__
已經從原來的'printName
變成了'wrapper'
:
>>> printName.__name__ 'wrapper' >>>
由於返回的那個wrapper()
函數名字就是'wrapper'
,因此,須要把原始函數的__name__
等屬性複製到wrapper()
函數中,不然,有些依賴函數簽名的代碼執行就會出錯。
不須要編寫wrapper.__name__ = func.__name__
這樣的代碼,Python內置的functools.wraps
就是幹這個事的,因此,一個完整的decorator的寫法以下:
1 >>> def log(func): 2 ... @functools.wraps(func) 3 ... def wrapper(*args,**kw): 4 ... print 'call %s():' %func.__name__ 5 ... return func(*args,**kw) 6 ... return wrapper 7 ... 8 >>>
也能夠針對呆參數的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
這是一個更新的例子,但願幫助你們理解:
1 >>> def deco(func): 2 ... def _deco(a,b): 3 ... print ("before myfunc() called.") 4 ... ret = func(a,b) 5 ... print("after called.result:%s" %ret) 6 ... return ret 7 ... return _deco 8 ... 9 10 >>> @deco 11 ... def myfunc(a,b): 12 ... print("myfunc(%s,%s) called."%(a,b)) 13 ... return a+b 14 ... 15 >>> myfunc(1,2) 16 before myfunc() called. 17 myfunc(1,2) called. 18 after called.result:3 19 3 20 >>>
補充說明:
其實,裝飾器就是一個函數,一個能夠用來包裝函數的函數,最後返回一個修改以後的函數(這裏的修改,好比增長日誌,如上例。)將其從新賦值原來的標識符,並永久喪失對原始函數對象的訪問。