裝飾器 decorator
標籤: python
這裏學習一下Python裝飾器(Decorator)的用法。
首先要注意的是,Python中的函數可以像普通變量一樣當做參數傳遞給另外一個函數,例如:
def foo():
print('foo')
def bar(func):
print('bar start')
func()
print('bar end')
bar(foo)
輸出爲:
bar start
foo
bar end
實際上裝飾器本質上是python的一個函數或類,它可以在其他函數或類在不需要修改代碼的前提下,附加某些額外功能。裝飾器的返回值也是一個函數或類的對象。
它經常用於有切面需要的場景,比如:插入日誌、性能測試、事務處理、緩存、教研權限等,裝飾器是一個絕佳的選擇。
我們可以抽離出與函數本身無關,但又在多個函數中存在需要的相同代碼作爲裝飾器,並繼續重用。
現在,假設我們要給foo()
增加功能,在執行函數時自動打印日誌,又不修改原函數的內容。
def log(func):
def wrapper(*args, **kw):
print('%s() is running...' % func.__name__)
return func(*args, **kw)
return wrapper
@log
def foo():
print('foo')
執行結果爲:
>>> foo()
foo() is running...
foo1
像這樣把@log
方法函數定義的開頭,相當於執行了語句:
foo = log(foo)
由於log()
是一個裝飾器,其返回一個函數。所以原來的foo()
函數依然存在,只是同名的函數指向了一個新的函數,於是調用foo()
將執行新的函數,即在log()
函數中返回的wrapper()
函數。
這裏的wrapper()
函數只是作爲一個原函數的代替。
wrapper()
函數的參數定義爲(*args, **kw)
,因此,
wrapper()
函數可以接收任意參數的調用。在wrapper()
函數內,首先打印日誌,然後執行原函數。
decorator需要傳入參數
如果Decorator本身需要傳入參數,那麼就需要編寫一個返回decorator的高階函數。比如要自定義log的文本。
def log(text):
def decorator(func):
def wrapper(*args, **kw):
print('%s() %s' % (func.__name__, text))
return func(*args, **kw)
return wrapper
return decorator
@log('is runningggggg ......')
def foo(text):
print(text)
>>> foo('sdfsdfsdfsdf')
foo() is runningggggg ......
sdfsdfsdfsdf
三層嵌套的實際效果爲:
foo = log(text)(foo)
返回函數的函數
log
返回decorator
,然後尋找decorator
的定義,將foo
作爲參數傳入,decorator
返回wrapper
,再尋找def wrapper()
,將foo
的參數傳入,打印日誌並返回foo(*args, **kw)
的結果。
我們來剖析上面的語句,首先執行log('execute')
,返回的是decorator
函數,再調用返回的函數,參數是foo
函數,返回值最終是wrapper
函數。
到這裏,實際上foo
已經被換做爲wrapper
了:
>>> foo.__name__
'wraper'
這樣實際上我們還是修改了一部分內容,當需要用到函數名字作爲依賴項時就會出現問題。
Python內置的functools.wraps
就可以解決這個問題,也就是執行了wrapper.__name__ = func.__name__
這樣的代碼。
一個完整的Decorator寫法如下:
import functools
def log(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
@log
def foo():
print('fooooo')
foo()
print(foo.__name__)
執行結果:
call foo():
fooooo
foo
帶參數的log:
import functools
def log1(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
@log1('ssssssssssss')
def foo1():
print('foo111111')
foo1()
foo1.__name__
結果:
ssssssssssss foo1()
foo111111
'foo1