一.装饰器
1.定义:
把一个函数当作参数,返回一个替代版的函数
本质就是一个返回函数的函数
也指在不改变原函数的基础上,给函数增加功能
举例如下:
装饰器的使用如下:
def desc(fun): ##装饰器
def add_info(): 装饰器内部函数
print('清明节快乐~')
fun() ##调用的函数
return add_info
@desc ##给该函数添加装饰器
def login():
print('login...')
@desc
def logout():
print('logout...')
login() #该函数的执行过程:在执行该函数时先返回该函数,发现该函数需要装饰器,再返回装饰器 ,首先识别return add_info 返回装饰器内部函数,执行装饰器内部要添加的内容,然后再执行该函数
logout()
效果如下:
2.特性
- 对修改时封闭的,对扩展时开放的
导入时间:(导入的是linux系统的时间)
举例如下:
效果如下:
上述例子添加装饰器优化如下:
当函数f1有装饰器,而f2没有装饰器时:
效果如下:
3.装饰器设置可变形参
举例说明如下:
效果如下:
4.装饰器设置关键字参数
举例说明如下:
效果如下:
5.装饰器练习1
- 装饰器实现一个函数计时器
问题1:被装饰的函数有返回值
问题2:如何保留被装饰函数的函数名和帮助信息文档
实现如下:
import time
import random
import string
import functools ##导入帮助文档
li = [random.choice(string.ascii_letters) for i in range(100)] ##产生内推码
print(li)
def Timer(fun):
"""这是一个装饰器"""
@functools.wraps(fun) ##帮助文档的调用
def wrapper(*args,**kwargs):
"""这是一个wrapper函数"""
start_time = time.time()
res = fun(*args,**kwargs)
end_time = time.time()
print('运行时间为:%.5f' %(end_time-start_time))
return res
return wrapper
@Timer
def con_add():
s = ''
for i in li:
s += (i + ',')
print(s)
@Timer
def join_add():
print(','.join(li))
@Timer
def fun_list(n):
"""这是一个fun_list函数"""
return [i ** 2 for i in range(n)]
@Timer
def fun_map(n):
return list(map(lambda x:x**2,range(n)))
con_add()
join_add()
print(fun_list(100))
print(fun_map(100))
运行如下:
- 不带函数返回值的
- 带有返回数值的
注意:当函数带有返回值时,要在装饰器内部添加其返回值
res = fun(*args,**kwargs) ##在装饰器内部将返回值赋值个给一个变量
return res #最后返回该数值
- 返回帮助文档的
注意:添加帮助文档后如果在执行某函数时使用帮助文档的参数,则会显示该函数的帮助文档 - 注释掉帮助文档
注意:在注释掉帮助文档但是调用了帮助文档之后,则返回时显示的是装饰器内部的函数的信息 - 标准时间的生成
6.帮助文档的使用
举例如下:
import time
import functools
def add_log(fun):
@functools.wraps(fun)
def wrapper(*args,**kwargs):
start_time = time.time()
res = fun(*args,**kwargs)
end_time = time.time()
print('[%s] 函数名: %s, 运行时间: %6f,运行返回值结果: %d'
%(time.ctime(),fun.__name__,(返回执行的函数的名字) end_time - start_time,res))
return res
return wrapper
@add_log
def add(x,y):
time.sleep(1)
return x + y
add(1,2)
6.装饰器练习2
- 判断是否为管理用户登录,如果是root,则显示相应的信息
insepect.getcallargs返回一个字典,key值是形参,value值是对应的实参
效果如下:
7.装饰器设置可变参数
题目要求:
编写装饰器required_types, 条件如下:
1). 当装饰器为@required_types(int,float)确保函数接收到的
每一个参数都是int或者float类型;
2). 当装饰器为@required_types(list)确保函数接收到的每一>个参数都是list类型;
3). 当装饰器为@required_types(str,int)确保函数接收到的每一个参数都是str或者int类型;
4). 如果参数不满足条件, 打印 TypeError:参数必须为xxxx类
型
实现如下:
import functools
def required_types(*kinds): ##设置装饰器接收多个参数
def required_int(fun):
@functools.wraps(fun)
def wrapper(*args, **kwargs):
for i in args: ##编历参数
if not isinstance(i, kinds): #判断参数类型是否为函数中给定的并且传到装饰器的参数
# print('TypeError:参数必须为',kinds)
# break
raise TypeError('参数必须为%s,%s' % kinds)
else:
res = fun(*args, **kwargs)
return res
return wrapper
return required_int ##给装饰器返回函数的指定的多个参数
@required_types(float, float)
def add(a, b):
return a + b
print(add(1.1, 2.0))
- 装饰器内部当报错返回值为print类型时的返回参数
- 当装饰器内部出错时使用raise返回参数
二.多个装饰器
1.多个装饰器的实现及其内部的执行顺序
执行效果如下:
由上结果可得:当有多个装饰器时,当定义一个函数使用多个装饰器时,首先装饰器外部的调用是自底向上的,而内部函数则是自顶向下的。
可分析上述结果:首先是从装饰器b,在到装饰器b的时候,发现装饰器外部要print,则执行该步,然后在到a装饰器,发现在改装饰器的外部同样也要执行相应的print,在执行完后就到装饰器的内部函数,则先执行b装饰器内部函数的内容,然后再执行b装饰器内部函数的内容。
2.多个装饰器练习
import functools
import inspect
def is_admin(fun):
@functools.wraps(fun)
def wrapper(*args,**kwargs):
#inspect.getcallargs返回一个字典,key值是形参,value值
#是对应的实参{'name':'root'}
inspect_res = inspect.getcallargs(fun,*args,*kwargs)
print('inspect的返回值: %s' %inspect_res)
if inspect_res.get('name') == 'root':
res = fun(*args,**kwargs)
return res
else:
print('not root user!')
return wrapper
login_session = ['root', 'redhat', 'westos']
def is_login(fun):
@functools.wraps(fun)
def wrapper(*args,**kwargs):
if args[0] in login_session:
res = fun(*args,**kwargs)
return res
else:
print('Error:%s未登录' %args[0])
return wrapper
@is_login
@is_admin
def add_student(name):
print('添加学生信息...')
add_student('linux')
执行效果如下: