装饰器(Decorator):是在不必改变原类文件和使用继承的情况下,动态的扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
1、常识问题
1 # =====第一部分========
2 def fun1():
3 for i in range(2):
4 print('第{0}次打印'.format(i))
5 # fun1 表示函数
6 # fun1()表示执行函数
7 # ======第二部分=======
8 def fun2():
9 print('fun2')
10 fun2 = lambda x:x*2
11 fun2(2) #执行的是下面的lambda表达式,而不是原来定义的fun2函数,因为fun2对象被重新定义了。
2、装饰器的应用
1.当我们需要为某个现有的对象,动态的增加一个新的功能或职责时,可以考虑使用装饰模式。
2.当某个对象的职责经常发生变化或者经常需要动态的增加职责,避免为了适应这样的变化,而增加继承子类扩展的方式,因为这种方式会造成子类膨胀的速度过快,难以控制。
3、基础实例讲解
示例如下:
1 def func1(func):
2 def inner():
3 print('验证过程')
4 return func()
5 return inner
6
7 @func1
8 def f1():
9 print('f1')
分析这段代码我们需要知道,函数并没有被执行。python解释器会从上到下来解释这段代码,步骤如下:
1、def func1(func): >>>将函数func1加载到内存
2、@func1
python解释器仅仅会解释这两句,因为函数在调用之前,其内部代码是不会被执行的。
@func1 这一句代码里却有大文章,@函数名 是python的一种语法糖。
如上例@func1内部会执行一下操作:
- 执行func1函数,并将 @func1 下面的 函数 作为func1函数的参数,即:@func1 等价于 func1(f1)
所以,内部就会去执行:
def inner():
print('验证过程')
return f1() # func是参数,此时 func 等于 f1
return inner # 返回的 inner,inner代表的是函数,非执行函数
其实就是将原来的 f1 函数塞进另外一个函数中 - 将执行完的 func1 函数返回值赋值给@func1下面的函数的函数名
func1函数的返回值是:
def inner:
print('验证过程')
return 原来f1() # 此处的 f1 表示原来的f1函数
然后,将此返回值再重新赋值给 f1,即:
新f1 = def inner:
print('验证过程')
return 原来f1()
所以,以后业务部门想要执行 f1 函数时,就会执行 新f1 函数,在 新f1 函数内部先执行验证,再执行原来的f1函数,然后将 原来f1 函数的返回值 返回给了业务调用者。
如此一来, 即执行了验证的功能,又执行了原来f1函数的内容,并将原f1函数返回值 返回给业务调用着,也可以理解为f1被重新定义了,现在执行的f1已经不是以前的f1了
4、装饰器进阶-带有参数的装饰器
1 def func1(func):
2 def inner(arg):
3 print('执行验证过程')
4 return func(arg)
5 return inner
6
7 @func1
8 def f1(arg):
9 print('执行验证过程{0}通过'.format(arg))
10 print('f1')
装饰器可以带有一个参数,二个参数等等。那么,可能有人要问装饰器是否可以带有不定的参数呢,答案是肯定的。
1 def func1(func):
2 def inner(*arg,**kwargs):
3 print('执行验证过程')
4 return func(*arg,**kwargs)
5 return inner
6
7 @func1
8 def f1(*arg,**kwargs):
9 print('执行验证过程{0}通过'.format(arg[0]))
10 print('f1')
5、装饰器进阶-多个装饰器
有人可能要问,一个函数可以被多个装饰器装饰吗,下面我们来具体看一下:
1 def func1(func):
2 def inner(*arg,**kwargs):
3 print('执行验证过程')
4 return func(*arg,**kwargs)
5 return inner
6
7 def func2(func):
8 def inner(*arg,**kwargs):
9 print('二次验证过程')
10 return func(*arg,**kwargs)
11 return inner
12
13 @func1
14 @func2
15 def f1(*arg,**kwargs):
16 print('执行验证过程{0}通过'.format(arg[0]))
17 print('f1')
这样可以实现多个装饰器的需求,但如果再多几个,这么做有显得不那么整洁,该怎么办呢?
1 def data_cleaning(request,kwargs):
2 print('First-Data Cleaning')
3
4 def data_log(request,kwargs):
5 print('Finally-Write into datalog')
6
7 def data_upload(before_func,after_func):
8 def decorator(main_func):
9 def wrapper(*arg,**kwargs):
10 before_result = before_func(*arg,**kwargs)
11 if(before_result != None):
12 return before_result
13
14 main_result = main_func(*arg,**kwargs)
15 if(main_result != None):
16 return main_result
17
18 after_result = after_func(*arg,**kwargs)
19 if (after_result != None):
20 return after_result
21 return wrapper
22 return decorator
23
24 @data_upload(data_cleaning,data_log)
25 def index(*arg,**kwargs):
26 print('index')
6、functools.wraps
上述的装饰器虽然已经完成了其应有的功能,即:装饰器内的函数代指了原函数,注意其只是代指而非相等,原函数的元信息没有被赋值到装饰器函数内部。和两层嵌套的decorator相比,三层嵌套的效果是这样的:
如5中的代码,data_upload(data_cleaning,data_log)(index)
data_upload(data_cleaning,data_log)返回的是decorator
函数,再调用返回的函数,参数是index
函数,返回值最终是wrapper
函数。
①python解释器在遇到@data_upload(data_cleaning,data_log)时,它会直接执行data_upload函数,并把它装饰的函数名当做参数,data_upload(data_cleaning,data_log)(index)
②执行了data_upload函数后,因其内部是一个函数decorator,它没有被调用,所以不执行,解释器返回了它的内存地址。index函数被重新定义
③新的index内部包含了before_func,after_func和main_func(即原来的index函数)
复杂装饰器的执行情况:
仍以5为例,
@data_upload(data_cleaning,data_log)
1、先执行data_upload(data_cleaning,data_log)函数,其返回一个decorator函数,此时@data_upload(data_cleaning,data_log),就变成了@decorator
2、@decorator,然后接着执行
3、新的index =decorator的返回值wrapper,这个里面有before_func,after_func和main_func(即原来的index函数)
def index(*arg,**kwargs): print('index')
这里面有一个问题:函数的 __name__ 属性发生了改变,导致有些依赖函数签名的代码执行会出错。具体来说,就是每个函数都有对象,它有__name__等属性,但经过Decorator装饰过的函数,它的__name__属性已经由原来的index变成了wrapper,
因为返回的那个wrapper()
函数名字就是'wrapper'
,所以,需要把原始函数的__name__
等属性复制到wrapper()
函数中,否则,有些依赖函数签名的代码执行就会出错。
不需要编写wrapper.__name__ = func.__name__
这样的代码,Python内置的functools.wraps
就是干这个事的,所以,一个完整的decorator的写法如下:
1 import functools
2
3 def outer(func):
4 @functools.wraps(func)
5 def inner(*args, **kwargs):
6 print(inner.__name__)
7 return func()
8 return inner
9
10 @outer
11 def function():
12 print('func')
13 function()
注释掉@functools.wraps(func)我们会发现所显示的__name__是不同的。
上面描述了这么多,只是为了理解它的原理,其实总结起来就一句话:记住在定义wrapper()
的前面加上@functools.wraps(func)
即可。
注:本博客为本人自学笔记,参考了以下站点。
http://www.cnblogs.com/wupeiqi/articles/4980620.html