【python】装饰器
来源:廖雪峰
看了好多次装饰器,发现还是廖老师讲得好,能让我看懂.....
下面是一段装饰器代码
@log def now(): print "20161107"
它的含义等价于
def now(): print "20161107" now = log(now)
即,log是一个函数,接收一个函数做参数,now变成了log(now)的返回值
下面,加上一个简单的log函数,只嵌套一层。
def log(func): print 'call %s():' % func.__name__ return func @log def now(): print "20161107"
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 "20161107" 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
可以看到,通过增加一层嵌套实现了装饰器参数