Python基础之装饰器

1、什么是装饰器?

装饰器本质上就是一个Python函数,它可让其它函数在不须要作任何代码变更的前提下,增长额外的功能,装饰器的返回值也是一个函数对象。python

装饰器的应用场景:好比插入日志,性能测试,事物处理,缓存等等场景。缓存

2、装饰器的造成过程

如今我有一个需求,我想让你在不改变函数代码的状况下,测试出这个函数的执行时间:app

import time


def func1():
    print("in func1")


def timer(func):
    def inner():
        start = time.time()
        func()
        print(time.time() - start)
    return inner


func1 = timer(func1)
func1()

可是若是有多个函数,我都想让你测试他们的执行时间,你岂不是每一个函数都要写一遍func1 = timer(func1)?ide

这样写很是麻烦,由于这些函数的函数名都是不相同的,有func一、func2,func3等等。函数

针对这种状况,python给咱们提供了一个简单快捷的方法,那就是语法糖。性能

 1 import time
 2 
 3 
 4 def timer(func):
 5     def inner():
 6         start = time.time()
 7         func()
 8         print(time.time() - start)
 9     return inner
10 
11 
12 @timer  # ==> func1 = timer(func1)
13 def func1():
14     print("in func1")
15     
16     
17 func1()
装饰器 ——语法糖

刚刚咱们讨论的装饰器都是装饰不带参数的函数,如今要装饰一个带参数的函数要怎么办呢?测试

 1 import time
 2 
 3 def timer(func):
 4     def inner(a):
 5         start = time.time()
 6         func(a)
 7         print(time.time() - start)
 8     return inner
 9 
10 
11 @timer
12 def func1(a):
13     print(a)
14 
15 func1(1)
装饰器——带参数的装饰器
 1 import time
 2 
 3 
 4 def timer(func):
 5     def inner(*args, **kwargs):
 6         start = time.time()
 7         result = func(*args, **kwargs)
 8         print(time.time() - start)
 9         return result
10     return inner
11 
12 
13 @timer #==> func1 = timer(func1)
14 def func1(a, b):
15     print("in func1")
16     
17 
18 @timer #==> func2 = timer(func2)
19 def func2(a):
20     print("in func2 and get a:%s" % a)
21     return "func2 end"
22 
23 
24 func1("aaa", "bbb")
25 print(func2("aa"))
装饰器——hold住全部参数的装饰器

上面的装饰器已经很是完美了,可是咱们在正常状况下查看函数信息的方法却在此处所有失效了:this

def index():
    """
    这是一个主页信息
    :return:
    """
    print("from index")


print(index.__doc__)    # 查看函数注释的方法
print(index.__name__)   # 查看函数名的方法

那么如何解决这个问题呢?spa

from functools import wraps


def deco(func):
    @wraps(func)    # 放在最内层函数最上方
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

@deco
def index():
    """
    显示首页信息
    :return:
    """
    print("from index")


print(index.__doc__)
print(index.__name__)

3、开放封闭原则

装饰器是完美的遵循了这个开放封闭原则的。设计

那么什么是开放封闭原则呢?

咱们能够从下面两个方面来看。

1. 对扩展是开放的

为何要对扩展开放呢?

咱们说,任何一个程序,不可能在设计之初就已经想好了全部的功能,而且将来不作任何更新和修改。因此咱们必须容许代码扩展、添加新功能。

2. 对修改是封闭的

为何要对修改封闭呢?

就像咱们刚刚提到的,由于咱们写的一个函数,颇有可能已经交付给其余人使用了,若是这个咱们对其进行了修改,颇有可能影响其余已经在使用该函数的用户。

4、装饰器的主要功能和固定结构

def timer(func):
    def inner(*args, **kwargs):
        """执行函数以前要作的"""
        result = func(*args, **kwargs)
        """执行函数以后要作的"""
        return result
    return inner
装饰器的固定格式——普通版
1 from functools import wraps
2 
3 def deco(func):
4     @wraps(func)    # 加在最内层函数最上方
5     def wrapper(*args, **kwargs):
6         return func(*args, **kwargs)
7     return wrapper
装饰器的固定格式——wrapper版

5、带参数的装饰器

假如你有成千上万个函数使用了一个装饰器,如今你想把这些装饰器都取消掉,你要怎么作?

若是一个一个的取消,那任务量也太大了吧。

万一,没过几天,你又须要用这些装饰器,岂不是要吐血。

那么解决办法,就是在装饰器上加上参数:

 1 def wrapper_out(flag):
 2     def wrapper(func):
 3         @wraps(func)
 4         def inner(*args, **kwargs):
 5             if flag:
 6                 print("执行函数以前要作的")
 7 
 8             result = func(*args, **kwargs)
 9 
10             if flag:
11                 print("执行函数以后要作的")
12 
13             return result
14         return inner
15     return wrapper
16 
17 
18 @wrapper_out(False)     # 经过传递True和False来控制装饰器内部的运行效果
19 def func():
20     pass
21 
22 
23 func()
带参数的装饰器

6、多个装饰器装饰一个函数

先执行下面这样一个代码

 1 def wrapper1(func):
 2     def inner(*args, **kwargs):
 3         print("111")
 4         result = func(*args, **kwargs)
 5         print("222")
 6         return result
 7     return inner
 8 
 9 
10 def wrapper2(func):
11     def inner(*args, **kwargs):
12         print("333")
13         result = func(*args, **kwargs)
14         print("444")
15         return result
16     return inner
17 
18 
19 @wrapper2
20 @wrapper1
21 def func():
22     print("this is func")
23 
24 func()
多个装饰器装饰一个函数

当执行完毕后,能够看到执行结果为:

333
111
this is func
222
444

执行顺序:首先@warpper1装饰器来,而后获取到一个新函数是wrapper1中的inner,而后执行@wrapper2。这个时候,wrapper2装饰的就是wrapper1中的inner了。

因此,执行顺序就像:第二层装饰器前(第一层装饰器前(目标)第一层装饰器后)第二层装饰器后。程序从左到右执行起来,这就是咱们看到的结果。

相关文章
相关标签/搜索