day11 python之函数装饰器

送分小仙女□ 提交于 2020-02-24 16:07:38

一,什么是装饰器?

装饰器本质上就是一个python函数,他可以让其他函数在不需要做任何代码变动的前提下,增加额外的功能,装饰器的返回值也是一个函数对象

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

二,装饰器的形成过程。

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

import time

def func1():
    print('in func1')

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

timer(func1)

上面的代码输出了func1()函数的运行时间,但是我们计算func1()函数的运行时间时,需要调用的是timer(func)函数

有没有办法直接调用func1()函数就能计算func1()函数运行时间呢?

如果让 func1 = timer 呢? 可是timer()函数需要一个函数参数,而我们的 func1 是不需要参数的,显然不行

那该怎么实现呢? 换个思路, timer不能赋值给func1,但是我们可以给timer指定一个函数返回值,将他赋值给func1

那么执行 func1() 时就相当于执行 这个用来赋值的函数.而这个函数在timer()函数内,可以调用timer()函数的参数func

如下所以,我们使用闭包的方法解决了这个问题

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)    # time()的返回值是inner
func1()        # func1()==inner()

在上面的方法中,timer()函数就是我们的装饰器函数,func1()是被装饰的.

但是,在上面的例子中,func1()是没有返回值和参数的,要是带上参数返回值呢?

import time    # 引入时间模块

def func1(a,b=1):    # 要测试执行时间的函数
    print('in func1')
    return 'func1的返回值'

def timer(func):
    def inner(*args,**kwargs):    # 设置为可以接受任意参数
        start = time.time()
        set = func(*args,**kwargs)
        print(time.time() - start)
        return set        # 返回func函数原本的返回值
    return inner

func1 = timer(func1)    # time()的返回值是inner
func1(a,b=3)        # func1(a,b=3)==inner(a,b=3)

但是如果有多个函数,我都想让你测试他们的执行时间,你每次是不是都得func1 = timer(func1)?这样还是有点麻烦,因为这些函数的函数名可能是不相同,有func1,func2,graph,等等,所以更简单的方法,python给你提供了,那就是语法糖。

@timer   # == func1 = timer(func1)

上面的装饰器已经非常完美了,但是有我们正常情况下查看函数信息的方法在此处都会失效:

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

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

如何解决这个问题呢?

from functools import wraps

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

@deco
def index():
    '''哈哈哈哈'''
    print('from index')

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

三,开放封闭原则。

1.对扩展是开放的

    为什么要对扩展开放呢?

    我们说,任何一个程序,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改。所以我们必须允许代码扩展、添加新功能。

2.对修改是封闭的

    为什么要对修改封闭呢?

    就像我们刚刚提到的,因为我们写的一个函数,很有可能已经交付给其他人使用了,如果这个时候我们对其进行了修改,很有可能影响其他已经在使用该函数的用户。

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

四,装饰器的主要功能和固定结构。

def timer(func):
    def inner(*args,**kwargs):
        '''执行函数之前要做的'''
        re = func(*args,**kwargs)
        '''执行函数之后要做的'''
        return re
    return inner
from functools import wraps

def deco(func):
    @wraps(func) #加在最内层函数正上方,确保查看函数信息的方法可用
    def wrapper(*args,**kwargs):
        '''执行函数之前要做的'''
        re = func(*args,**kwargs)
        '''执行函数之后要做的'''
        return re
    return wrapper
装饰器的固定格式--wraps版

五,带参数的装饰器。

注意@outer(False)的用法

上面的@timer   相当于   func = timer(func)  ,  返回值是 inner

下面的@outer(False)   相当于  func = outer(False)(func)  ,返回值仍是 inner

def outer(flag):
    def timer(func):
        def inner(*args,**kwargs):
            if flag:
                print('''执行函数之前要做的''')
            re = func(*args,**kwargs)
            if flag:
                print('''执行函数之后要做的''')
            return re
        return inner
    return timer

@outer(False)    #此处相较上面的语法糖,多了括号和参数.@outer(False)==>func=outer(False)(func),返回值仍是inner
def func(): 
    print(111)

func()    

六,多个装饰器装饰一个函数。

def wrapper1(func):
    def inner():
        print('wrapper1 ,before func')
        func()
        print('wrapper1 ,after func')
    return inner

def wrapper2(func):
    def inner():
        print('wrapper2 ,before func')
        func()
        print('wrapper2 ,after func')
    return inner

@wrapper2
@wrapper1
def f():
    print('in f')

f()
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!