1. 装饰器的定义
就是给已有函数增加额外功能的函数,它本质上就是一个闭包函数。
装饰器的功能特点:
- 不修改已有函数的源代码
- 不修改已有函数的调用方式
- 给已有函数增加额外的功能
闭包和装饰器的区分:
如果闭包函数的参数有且只有一个,并且是函数类型,那么这个闭包函数称之为装饰器。
写代码要遵循开放封闭原则,它规定已经实现的功能代码不允许被修改,但可以被扩展。
2. 装饰器的示例代码
# 定义装饰器def decorator(func):def inner():# 在内部函数里面对已有函数进行装饰print('已添加登录认证')func()return innerdef comment():print('发表评论')# 调用装饰器对已有函数进行装饰,左边的comment=innercomment = decorator(comment)# 调用方式不变comment()
3. 装饰器的语法糖写法
如果有多个函数都需要添加登录验证的功能,每次都需要编写func = decorator(func)这样代码对已有函数进行装饰,这种做法还是比较麻烦。
Python给提供了一个装饰函数更加简单的写法,那就是语法糖,语法糖的书写格式是: @装饰器名字,通过语法糖的方式也可以完成对已有函数的装饰
# 定义装饰器def decorator(func):def inner():# 在内部函数里面对已有函数进行装饰print('已添加登录认证')func()return inner@decorator # comment = decorator(comment) 装饰器语法糖对该代码进行了封装 左边comment=innerdef comment():print('发表评论')# 调用方式不变comment()
4. 装饰器的执行时机
当 当前模块加载完成以后,装饰器会立即执行,对已有函数进行装饰。
# 定义装饰器def decorator(func):print('装饰器执行了')def inner():# 在内部函数里面对已有函数进行装饰print('已添加登录认证')func()return inner@decorator # comment = decorator(comment) 装饰器语法糖对该代码进行了封装 左边comment=innerdef comment():print('发表评论')
运行结果:
装饰器执行了
5. 装饰器的使用
5.1 装饰器的使用场景
- 函数执行时间的统计
- 输出日志信息
5.2 装饰器实现已有函数执行时间的统计
import timedef decorator(func):def inner():# 获取时间距离1970-1-1 0:0:1的时间差begin = time.time()func()end = time.time()result = end - beginprint(f'函数执行完成耗时:{result}')return inner@decoratordef work():for i in range(10000):print(i)work()
6. 通用装饰器的使用
通用装饰器:可以装饰任意类型的函数
使用装饰器装饰已有函数的时候,内部函数的类型和要装饰的已有函数的类型保持一致
6.1 装饰带有参数的函数
def decorator(func):def inner(num1, num2):print('正在努力执行加法计算')func(num1, num2)return inner@decoratordef add_num(num1, num2):result = num1 + num2print(f'结果为:{result}')add_num(1, 2)
6.2 装饰带有参数、返回值的函数
def decorator(func):def inner(num1, num2):print('正在努力执行加法计算')num = func(num1, num2)return numreturn inner@decoratordef add_num(num1, num2):result = num1 + num2return resultresult = add_num(1, 2)print(f'结果为:{result}')
6.3 装饰带有不定长参数、返回值的函数
def decorator(func):def inner(*args, **kwargs):print('正在努力执行加法计算')# *args:把元组里面的每一个元素,按照位置参数的方式进行传参# **kwargs:把字典里面的每一个键值对,按照关键字的方式进行传参num = func(*args, **kwargs)return numreturn inner@decoratordef add_num(*args, **kwargs):result = 0for value in args:result += valuefor value in kwargs.values():result += valuereturn resultresult = add_num(1, 2, a=3)print(f'结果为:{result}')
7. 多个装饰器的使用
多个装饰器的装饰过程:由内到外的一个装饰过程,先执行内部的装饰器,在执行外部的装饰器。
def make_div(func):print('make_div装饰器执行了')def inner():result = '<div>' + func() + '</div>'return resultreturn innerdef make_p(func):print('make_p装饰器执行了')def inner():result = '<p>' + func() + '</p>'return resultreturn inner# 原理剖析:content = make_div(make_p(content))# 分布拆解:content = make_p(content),内部装饰器完成,content = make_p.inner# content = make_div(make_p.inner)@make_div@make_pdef content():return '人生苦短,我用python'c = content()print(c)
make_p装饰器执行了make_div装饰器执行了<div><p>人生苦短,我用python</p></div>
8. 带有参数的装饰器
带有参数的装饰器就是使用装饰器装饰函数的时候可以传入指定参数,语法格式: @装饰器(参数,…)
使用带有参数的装饰器,其实是在装饰器外面又包裹了一个函数,使用该函数接收参数,返回是装饰器,因为 @ 符号需要配合装饰器实例使用。
def return_decorator(flag):# 装饰器只能接收一个参数并且是函数类型def decorator(func):def inner(a, b):if flag == '+':print('正在努力执行加法计算')elif flag == '-':print('正在努力执行减法计算')func(a, b)return inner# 当调用函数的时候可以返回一个装饰器decoratorreturn decorator@return_decorator('+') # decorator = return_decorator('+'), @decorator => add_num = decorator(add_num)def add_num(a, b):result = a + bprint(result)@return_decorator('-')def sub_num(a, b):result = a - bprint(result)add_num(1, 2)sub_num(1, 2)
正在努力执行加法计算3正在努力执行减法计算-1
9. 类装饰器的使用
类装饰器:使用类装饰已有函数
class MyDecorator(object):def __init__(self, func):self.__func = func# 实现__call__方法,表示对象是一个可调用对象,可以像调用函数一样进行调用def __call__(self, *args, **kwargs):# 对已有函数进行封装print('马上就有下班啦')self.__func()@MyDecorator # @MyDecorator => show = MyDecorator(show)def show():print('快要下雪啦')# 执行show,就相当于执行MyDecorator类创建的实例对象,show() => 对象()show()
马上就有下班啦快要下雪啦
扩展:
函数之所以能够调用,是因为函数内部实现了
__call__
方法
10. 应用场景
收集函数的操作或错误日志记录
验证函数的使用权限
计算函数的运行时间
在ORM/DB模型操作时,通过属性方法动态地获取关联的数据
函数数据的缓存
定制函数的输入和输出(序列化和反序列化)