python装饰器(装饰器模式)

本节内容要先看 python闭包

装饰器,英文:decorator

作用

在 不改动源代码 的情况下,在执行函数 前和后 增加额外 功能

好处

将与函数功能 无关的代码, 抽离出来 ,封装成装饰器

在JAVA等语言中也有类似的功能,称之为 aop ,Aspect Oriented Programming 的缩写,意为:面向切面编程

装饰器 (decorator)是23种 设计模式 之一

应用场景

  1. 打印日志
  2. 性能测试
  3. 权限校验等
  4. 数据库事务处理

实现原理

本质上是闭包

实现

装饰器的原理就是 闭包

#传入函数对象func
def 装饰器名(func):
    #定义内部函数,不定长参数,关键字参数
    def wrapper(*args,**kwargs):
        #执行传入的函数对象
        ret = func(*args,**kwargs)
        #返回结果
        return ret
    #返回内部函数
    return wrapper

总结:

  1. 定义外部函数,形参是 函数对象
  2. 定义内部函数,形参是 不定长参数,关键字参数,并 返回内部函数
  3. 内部函数调用传入的函数对象,并将 不定长参数,关键字参数 传入
  4. 将函数对象执行的结果返回

通过调用函数执行

定义被测试的函数,并通过 高阶函数 执行,代码如下:

#定义函数
def test(args):
    pass

#通过调用函数执行
装饰器名(test)(123)

执行过程:

  1. 执行 装饰器 函数,并将test函数对象传入,并返回wrapper函数对象
  2. wrapper函数对象后面是(123),表示执行wrapper函数,并传入123
  3. 在wrapper函数中执行传入的test函数,并向test函数传入参数123
  4. 将test函数执行的结果返回

通过@装饰器执行

@装饰器名
def test(args):
    pass

test(123)

在test()函数上面加上@装饰器名

执行过程:
同上

打印日志的装饰器

在执行函数之前,打印日志,日志格式如下:

----------- 开始执行 login(), 参数: 传入的参数

定义 装饰器log (其实就是高阶函数)

def log(func):
    def wrapper(*args, **kw):
        print('-'*20,'开始执行 %s(),' % func.__name__,'参数:',args)
        result= func(*args, **kw)
        return result
    return wrapper

通过调用函数执行

定义被测试的函数,并通过 高阶函数 执行,代码如下:

def login(username,password):
    print("用户名:%s,密码:%s"%(username,password))

log(login)('admin','123456')

执行过程:

  1. 执行log()高阶函数,并将login函数对象传入,并返回wrapper函数对象
  2. 由于后面是(‘admin’,’123456’),会直接运行wrapper函数,并传入admin和123456
  3. 在wrapper函数中执行打印,然后执行传入的login函数,并向login函数传入参数
  4. 将login函数执行的结果返回

通过@装饰器执行

定义被测试的函数,并通过 装饰器 执行,代码如下:

@log
def login(username,password):
    print("用户名:%s,密码:%s"%(username,password))

login('admin','123456')

在login()函数上面加上@log即可

执行过程:
同上

关于函数名

在使用装饰器时,如果打印函数对象,如下:

print(login.__name__)

其结果是wrapper,而不是login,因为返回的是wrapper函数对象。在此处虽然不影响执行结果,但在使用函数签名,获取函数形参等信息时,会引起bug

关于函数签名见链接:
http://www.malaoshi.top/show_1EF2LrZpVIRm.html

通过pthon内置的 functools.wraps() 装饰器实现改函数名的操作,放在 最里面内部函数的上面,改造如下:

import functools
def log(func):
    @functools.wraps(func)
    def wrapper(*args, **kw):
        print('-'*20,'开始执行 %s(),' % func.__name__,'参数:',args)
        result= func(*args, **kw)
        return result
    return wrapper

测试调用

print(log(login).__name__)

返回函数名为login

练习

测试函数性能

在编码中,要测试函数性能,比如不能超过5秒钟,实现原理如下:

  1. 在函数的开始处,获取当前时间
  2. 在函数的末尾处,获取当前时间
  3. 末尾时间减去开始时间,得到的秒数就是该函数执行的总时长

代码如下:

import datetime
import time
def login(username,password):
    t=datetime.datetime.now()
    result=None
    if username=='admin' and password=='123456':
        result=True
    else:
        result=False
    #代码太少,间隔时间为0,所以人为休眠
    time.sleep(1)
    t2=datetime.datetime.now()
    print("总时长:%s"%(t2-t).total_seconds())
    return result

result=login('admin','123456')

用装饰器实现测试函数性能

实现性能装饰器,思路:在函数执行前记录时间,在函数执行后再次记录时间,第二个时间减去前一个时间,间隔的秒数就是该函数的执行时间

#定义装饰器
def testtime(func):
    def wrapper(*args, **kw):
        starttime=datetime.datetime.now()
        fun= func(*args, **kw)
        endtime=datetime.datetime.now()
        print('-'*20,(endtime-starttime).total_seconds(),'-'*20,'结束执行 %s():' % func.__name__)
        return fun
    return wrapper

@testtime
def login(username,password):
    print("用户名:%s,密码:%s"%(username,password))
    time.sleep(0.1)

#调用执行
login('aaa','bbb')

原文出处:/show_1EF2JFtzG8iw.html