裝飾器:
1.用于裝飾其他函數(shù)
2.增強(qiáng)被裝飾函數(shù)的功能
裝飾器需要接受一個(gè)函數(shù)對(duì)象作為參數(shù),以對(duì)其進(jìn)行增強(qiáng)
def deco(func):
def wrapper():
print "decorate something"
func()
print "decorate finish"
return wrapper
@deco
def test_func():
return "test func"
test_func()
output:
decorate something
test func
decorate finish
test_func()被裝飾之后,運(yùn)行函數(shù)test_func(),會(huì)運(yùn)行裝飾器這個(gè)函數(shù)
當(dāng)你在用某個(gè)@decorator來修飾某個(gè)函數(shù)func時(shí),如下所示:
@decorator
def func():
pass
其解釋器會(huì)解釋成下面這樣的語句:
func = decorator(func)
先看一個(gè)簡單的裝飾器如下:
def hello(fn):
def wrapper():
print "hello, %s" % fn.__name__
fn()
print "goodby, %s" % fn.__name__
return wrapper
@hello
def foo():
print "i am foo"
foo()
輸出:
hello, foo
i am foo
goodby, foo
對(duì)于一個(gè)裝飾器的定義:
@hello
def foo():
print "i am foo"
實(shí)際上可以理解成foo = hello(foo),將被包裝的函數(shù)(foo)當(dāng)作參數(shù)傳入裝飾器(hello)中,通過一系列包裝后將包裝后的函數(shù)再返回給被包裝的函數(shù)(foo),實(shí)際上就是返回了一個(gè)新函數(shù),只不過是根據(jù)原函數(shù)改造而來并且名字不變。
def out_func(**kwds):
def real_decorator(fn):
arg = kwds['arg1'] + kwds['arg2']
def wapper():
fn()
return kwds['arg1'] + kwds['arg2'] + arg
return wapper
return real_decorator
@out_func(arg1='11111', arg2='22222')
def hello_world():
print('hello world')
print(hello_world())
分析:我的理解是裝飾器有兩層,內(nèi)層wapper用于包裝,外層decorator(real_decorator)用于接收被包裝的函數(shù)(hello_world)作為參數(shù),并將包裝后的函數(shù)(wapper)返回給外部。此處的例子有三層,外層還有一個(gè)out_func函數(shù)的用處是給內(nèi)層傳遞參數(shù),由于外部多了一層函數(shù)所以裝飾器函數(shù)(real_decorator)將裝飾器本身返回給out_func,于是@out_func就相當(dāng)于裝飾器。
functools.wraps
我們?cè)谑褂?Decorator 的過程中,難免會(huì)損失一些原本的功能信息。
def logged(func):
def with_logging(*args, **kwargs):
print func.__name__ + " was called"
return func(*args, **kwargs)
return with_logging
@logged
def f(x):
"""does some math"""
return x + x * x
def f(x):
"""does some math"""
return x + x * x
f = logged(f)
In [24]: f.__name__
Out[24]: with_logging
由于返回的是包裝后的with_logging,所以name等基本信息變成了返回的新函數(shù)
而functools.wraps 則可以將原函數(shù)對(duì)象的指定屬性復(fù)制給包裝函數(shù)對(duì)象, 默認(rèn)有 module、name、doc,或者通過參數(shù)選擇。代碼如下:
from functools import wraps
def logged(func):
@wraps(func)
def with_logging(*args, **kwargs):
print func.__name__ + " was called"
return func(*args, **kwargs)
return with_logging
@logged
def f(x):
"""does some math"""
return x + x * x
print f.__name__ # prints 'f'
print f.__doc__ # prints 'does some math'
from functools import wraps
def logged(func):
@wraps(func)
def with_logging(*args, **kwargs):
print func.__name__ + " was called"
return func(*args, **kwargs)
return with_logging
@logged
def f(x):
"""does some math"""
return x + x * x
print f.__name__ # prints 'f'
print f.__doc__ # prints 'does some math'