装饰器可以用于的一种场景是插入日志,比如在代码中执行某一个函数之前在日志中打印出该函数名,如果该函数执行出错,只要看一眼函数名就可以快速的定位到出现错误的函数,如果连带当时执行的函数形参也打印出来就会更方便了。装饰器和闭包有一些关联,可以先了解一下闭包再了解装饰器。
#-*- coding:utf-8 -*-
def decorator(func):
def wrapper():
print "#############"
return wrapper
@decorator
def test():
print "test ........"
if __name__ == '__main__':
test()
上述代码执行的结果是##########,函数test中的代码没有执行。先了解一下装饰器的工作流程:主函数调用test函数,在test函数执行之前会先执行装饰器,装饰器函数decorator返回一个wrapper函数,wrapper函数执行,执行完以后就直接结束了。根据装饰器的解释应该是先执行装饰器然后执行test函数,现在的执行结果完全不是这样啊 。造成这样情况出现的原因是应为wrapper函数在执行完之后没有返回要执行的test函数(也就是func形参)。上述的装饰器不是一个标准的装饰器,只能是一个返回函数的高阶函数。
在上述wrapper函数中添加一个return 返回函数func就是一个简单的装饰器了(内层函数的参数以及装饰器的形参最好与使用者的函数形参保持一致)
一、普通函数的装饰器
(1)没有参数的装饰器
#-*- coding:utf-8 -*-
def decorator(func):
def wrapper():
print "#############"
return func()
return wrapper
@decorator
def test():
print "test ........"
if __name__ == '__main__':
test()
将上面的两段代码执行过之后比较结果,可以看到先执行wrapper函数,然后又执行了test函数语句
(2)调用函数有多个形参,形参的个数不确定,(给内层函数wrapper添加一个可变参数和关键字参数)装饰器的实现方式:
#-*- coding:utf-8 -*-
def decorator(func):
def wrapper(*args,**kw):
print "wrapper.........."
print "function--->%s" % func.__name__
for x in args:
print x
return func(*args,**kw)
return wrapper
@decorator
def test1():
print "test1 ........"
@decorator
def test2(a,b):
print "test2........."
@decorator
def test3(a,b,c):
print "test3........."
if __name__ == '__main__':
test1()
test2(1,2)
test3(1,2,3)
(3)多个装饰器的使用,直接在函数前按照顺序添加就可以,装饰器按照顺序执行
#-*- coding:utf-8 -*-
def decorator(func):
def wrapper(*args,**kw):
print "wrapper.........."
print "function--->%s" % func.__name__
for x in args:
print x
return func(*args,**kw)
return wrapper
def decorator_test(func):
def wrapper_test(*args,**kw):
print "wrapper!!!!!!!!!!!!!!!!"
print "function--->%s" % func.__name__
for x in args:
print x
return func(*args,**kw)
return wrapper_test
@decorator
@decorator_test
def test1():
print "test1 ........"
if __name__ == '__main__':
test1()
执行结果:
wrapper..........
function--->wrapper_test
wrapper!!!!!!!!!!!!!!!!
function--->test1
test1 ........
仔细看上面的执行结果,首先执行的是装饰器decorator,装饰器的形参值是第二个装饰器的wrapper_test函数,装饰器decotor_test的形参是test1(被装饰函数) ,装饰器函数每次都会取调用自己处的被装饰函数作为形参,并在wrapper执行后执行该被装饰函数。
(4)装饰器函数本身如果需要除传入函数形参之外的其他形参,在实现的时候就需要借助高阶函数返回装饰器实现。
#-*- coding:utf-8 -*-
def function_decorator(*arg):
def decorator(func):
def wrapper(*args,**kw):
for a in arg:
print "text--->%s" % a
print "wrapper.........."
print "function--->%s" % func.__name__
for x in args:
print x
return func(*args,**kw)
return wrapper
return decorator
@function_decorator("textaaaa",123)
def test1(a,b):
print "test1 ........"
if __name__ == '__main__':
test1(1,2)
上面的函数代码都是针对普通的函数装饰器,最后一个装饰器能够满足大多数的插入日志的实现了
二、类与装饰器
(1)类方法的装饰器
与普通函数装饰器相比较类方法装饰器内层函数需要一个self参数,因为类方法本身就有一个self参数,所以也需要给一个对应的形参
#-*- coding:utf-8 -*-
def decorator(func):
def wrapper(self,*args,**kw):
print "wrapper......"
print "self id: %s" % id(self)
for x in args:
print x
return func(self,*args,**kw)
return wrapper
class student(object):
"""docstring for student"""
def __init__(self):
super(student, self).__init__()
@decorator
def class_fun(self,a,b):
print "class_fun......"
if __name__ == '__main__':
stu = student()
stu.class_fun(1,2)
print "stu id : %s" % id(stu)
上面代码执行的结果:
wrapper......
self id: 44454896
1
2
class_fun......
stu id : 44454896
观察上面代码执行的结果可以看出装饰器内层函数的self就是类student的实例stu的对象,也就是把实例对象传递给了内层函数。
(2)装饰器类
定义一个装饰器类去修饰函数。装饰器类是借助于重构类的内置方法__call__实现。__call__()是一个特殊方法,它可将一个类实例变成一个可调用对象:
#-*- coding:utf-8 -*-
class Decorator(object):
"""docstring for Decorator"""
def __init__(self,func):
super(Decorator, self).__init__()
self.func = func
def __call__(self,*args,**kw):
print "__call__......."
print "self.func---->%s" % self.func.__name__
for x in args:
print x
return self.func(*args,**kw)
@Decorator
def test(a,b):
print "test........"
if __name__ == '__main__':
test(1,2)