python装饰器

装饰器(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

 

posted @ 2017-03-17 15:10  jasonli_01  阅读(211)  评论(0编辑  收藏  举报