来源:廖雪峰
看了好多次装饰器,发现还是廖老师讲得好,能让我看懂.....
下面是一段装饰器代码
@log
def now():
print ""
它的含义等价于
def now():
print "" now = log(now)
即,log是一个函数,接收一个函数做参数,now变成了log(now)的返回值
下面,加上一个简单的log函数,只嵌套一层。
def log(func):
print 'call %s():' % func.__name__
return func @log
def now():
print ""
print "-----"
now()
结果
call now():
-----
20161107
在log函数中打印了被调用函数的名称,但是一共只会运行一次,在定义的时候。之后每次运行now函数结果和不加装饰器相同。
两层嵌套
def log(func):
def wrapper(*args, **kw):
print 'call %s():' % func.__name__
return func(*args, **kw)
return wrapper @log
def now():
print "" print "-----"
now()
now()
print now.__name__
结果
-----
call now():
20161107
call now():
20161107
wrapper
可以看到,在两层嵌套中,可以实现每次运行now函数时都打印函数名。
在用log装饰后,now=log(now) 也就是wrapper函数,wrapper函数中封存了原本的now函数,采用可变参数,保证wrapper可以接收now函数的变量。wrapper中会先打印函数名,然后返回原本now函数的结果。
问题是,在打印名称时,now.__name__已经变成了wrapper。需要把原始函数的__name__
等属性复制到wrapper()
函数中,否则,有些依赖函数签名的代码执行就会出错。
不需要编写wrapper.__name__ = func.__name__
这样的代码,Python内置的functools.wraps
就是干这个事的,所以,一个完整的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("123")这样
下面是例子
def log(*args0, **kw0):
def decorator(func):
def wrapper(*args1, **kw1):
if len(args0) == 0:
print "args0 = None"
else:
print args0
if len(kw0) == 0:
print "kw0 = None"
else:
print kw0
return func(*args1, **kw1)
return wrapper
return decorator @log("huhuhuhu")
def a(x, y):
print "A"
return x + y @log()
def b():
print "B" @log("c", t=1, e=2, s=3)
def c():
print "C" A = a(3,2)
print A
print "------------"
b()
print "------------"
c()
结果
('huhuhuhu',)
kw0 = None
A
5
------------
args0 = None
kw0 = None
B
------------
('c',)
{'s': 3, 'e': 2, 't': 1}
C
@log("huhuhuhu")的含义为:
now = log("huhuhuhu")(now)
= decorator(now)
= wrapper
可以看到,通过增加一层嵌套实现了装饰器参数