一、装饰器的概念
装饰器实际上就是为了给某程序增添功能,但该程序已经上线或已经被使用,那么就不能大批量的修改源代码,这样是不科学的也是不现实的,因为就产生了装饰器,使得其满足:
- 不能修改被装饰的函数的源代码
- 不能修改被装饰的函数的调用方式
- 满足1、2的情况下给程序增添功能
那么根据需求,同时满足了这三点原则,这才是我们的目的。
装饰器的原则组成:
< 函数+实参高阶函数+返回值高阶函数+嵌套函数+语法糖 = 装饰器 >
这个式子是贯穿装饰器的灵魂所在!
装饰器的应用场景:
计时器、记录日志、用户登陆验证、函数参数验证
二、装饰器的使用
1.普通的装饰
把函数看成是盒子,a是小盒子,inner是中盒子,outer是大盒子。程序中,把小盒子a传递到大盒子outer中的中盒子inner,然后再把中盒子inner拿出来,调用
2.语法糖
只要在函数前加上类似于@desc ,就可以实现装饰作用 这种方法被称为语法糖。
3.装饰有参函数
上面介绍的两个简单示例都是无参的
1)装饰器的需求:获取每个函数的执行时间
函数执行之前计算时间
函数执行之后计算时间
注:我们知道a = fun = test,因此a()=fun()=test()
,那么当at()有参数时,就必须给fun()和test()也加上参数,为了使程序更加有扩展性,因此在装饰器中的test()和fun(),加了可变参数*agrs和 关键字参数**kwargs。
2) 在1)的基础上解决以下问题
问题1:被装饰的函数有返回值的时候怎么办?
问题2:如何保留被装饰函数的函数名和帮助文档信息
先导一个functools的包,再执行@functools.wraps
在被装饰的函数这可以用__name__ __doc__ 测试(如下图)
4.带有参数的装饰器
又增加了一个需求,一个装饰器,对不同的函数有不同的装饰。那么就需要知道对哪个函数采取哪种装饰。因此,就需要装饰器带一个参数来标记一下。例如:@functool.wraps
注:获取当前时间的三个函数:
time.time():获取当前时间,浮点数表示,从当前系统开始使用到现在所经历的秒数
time.ctime():获取当前时间,字符串表示,易读的格式
time.gmtime():获取当前时间,计算机程序可处理的格式
5.python内置函数:map()
介绍一下在3中提到的map()代码
map():接收两个参数,一个是函数,一个是序列。map依次将序列传递给函数,并把结果作为新的列表返回。
6.装饰器的应用场景
1)计时器:如3中的例子
2)记录日志
创建装饰器, 要求如下:
# 1. 创建add_log装饰器, 被装饰的函数打印日志信息;
# 2. 日志格式为: [字符串时间] 函数名: xxx, 运行时间:xxx, 运行返回值结果:xxx
3)用户登陆验证
需求: 用户登陆验证的装饰器is_login
# # 1). 如果用户登陆成功, 则执行被装饰的函数;
# # 2). 如果用户登陆不成功, 则执行登陆函数
方法:导入functools 包,设定一个用户名单,在装饰器里作判断
# #需求: 判断登陆用户是否未管理员is_admin(此处管理员只有一个为:admin用户)
# # 1).如果用户为管理员, 则执行被装饰的函数;
# # 2).如果用户不是管理员, 则报错;
4)函数参数的验证
编写装饰器required_ints, 条件如下:
# 1). 确保函数接收到的每一个参数都是整数;
# 2). 如果参数不是整形数, 打印 TypeError:参数必须为整形
升级版(有参数的装饰器)
# 编写装饰器required_types, 条件如下:
# 1). 当装饰器为@required_types(int,float)确保函数接收到的每一个参数都是int或者float类型;
# 2). 当装饰器为@required_types(list)确保函数接收到的每一个参数都是list类型;
# 3). 当装饰器为@required_types(str,int)确保函数接收到的每一个参数都是str或者int类型;
# 4). 如果参数不满足条件, 打印 TypeError:参数必须为xxxx类型