一、概念
1、定义一个函数(类),这个函数的功能就是用来装饰其他函数的;(这个函数是用来给其他函数添加额外的功能的);
2、开放封闭原则:
(1)开放:对扩展功能(增加功能)开放。扩展功能的意思是在源代码不做任何改变的情况下,为其增加功能;
(2)封闭:对修改源代码是封闭的;
3、装饰器:不修改被装饰对象的源代码,也不修改调用方式的前提下,给被装饰对象添加新的功能;
插曲:
运用了time.time()=计算现在距unix元年的时间(1970年1月1日8点0分0秒)
二、无参装饰器例子:
# 源代码
def inside(group, s):
print('欢迎来到王者荣耀')
print(f'你出身在{group}阵营')
print(f'敌军还有{s}秒到达战场')
time.sleep(s)
print('全军出击')
inside('红色', 5)
运行结果:
欢迎来到王者荣耀
你出身在红色阵营
敌军还有5秒到达战场
全军出击
# 统计上述代码运行时间
方案一:没有修改调用方式,修改了源代码
# 方案一
# 没有修改调用方式,修改了源代码
def inside(group, s):
start =time.time()
print('欢迎来到王者荣耀')
print(f'你出身在{group}阵营')
print(f'敌军还有{s}秒到达战场')
time.sleep(s)
print('全军出击')
end =time.time()
print(end-start)
inside('红色', 5)
运行结果:
欢迎来到王者荣耀
你出身在红色阵营
敌军还有5秒到达战场
全军出击
5.010248899459839
方案二:既未修改源代码,又未修改调用方式;但是若该函数调用多次,代码冗余
def inside(group, s):
print('欢迎来到王者荣耀')
print(f'你出身在{group}阵营')
print(f'敌军还有{s}秒到达战场')
time.sleep(s)
print('全军出击')
start =time.time()
inside('红色', 5)
end =time.time()
print(end-start)
方案三:解决了方案二的代码冗余问题,没有修改源代码,同时增加新功能,但被装饰对象的调用方式被修改
def inside(group, s):
print('欢迎来到王者荣耀')
print(f'你出身在{group}阵营')
print(f'敌军还有{s}秒到达战场')
time.sleep(s)
print('全军出击')
def wrapper(): # wrapper;装饰器,装饰品
start = time.time()
inside('红色', 5)
end = time.time()
print(end - start)
wrapper()
方案四:解决方案三的问题,解决了传参数量的问题,但是只针对inside函数
def inside(group, s):
print('欢迎来到王者荣耀')
print(f'你出身在{group}阵营')
print(f'敌军还有{s}秒到达战场')
time.sleep(s)
print('全军出击')
def wrapper(*args, **kwargs): # 无论给wrapper函数传多少个参数,均可成功传参
start = time.time()
inside(*args, **kwargs)
end = time.time()
print(end - start)
wrapper()
方案五:
def inside(group, s):
print('欢迎来到王者荣耀')
print(f'你出身在{group}阵营')
print(f'敌军还有{s}秒到达战场')
time.sleep(s)
print('全军出击')
# 通过闭包的方式传参
def outer(func):
# func =inside # 将函数的内存地址作为参数传,例如这里只能是inside
def wrapper(*args, **kwargs): # 无论给wrapper函数传多少个参数,均可成功传参
start = time.time()
func(*args, **kwargs)
end = time.time()
print(end - start)
return wrapper # 这里加(),就是直接调用wrapper,得到的是wrapper的返回值;
# 不加(),得到的是wrapper的内存地址,全局就可以访问wrapper
# wrapper的内存地址就是outer的返回值,也就是res
res = outer(inside) # 通过传参的方式,res也可换成inside
# 调用wrapper,给wrapper传参=给装饰对象传参
res('蓝色',3)
补充:
(1)
def charge (num): for i in range(num,101): time.sleep(0.05) print(f'当前电量:{i}%') print('电量已充满') charge(1)
运行结果:
(2)
/r :回车,调到当前行的开头
print 默认在最后带一个换行符,通过end=‘ ’,来处理
def charge(num): for i in range(num, 101): time.sleep(0.05) print(f'\r当前电量:{i}%', end='') print('电量已充满') charge(2)
# 运行结果
(3)
def charge(num): for i in range(num, 101): time.sleep(0.05) # 制作进度条,字符串可以做乘法计算,一个字符串X10 就是10个字符串 print(f'\r当前电量:{"◇"*i} {i}%', end='') # 外面用单引号,里边不可用单引号 print('电量已充满') charge(2)
运行结果;
三、无参装饰器有返回值,例子-2,装饰对象有返回值
def charge(num):
for i in range(num, 101):
time.sleep(0.05)
# 制作进度条,字符串可以做乘法计算,一个字符串X10 就是10个字符串
print(f'\r当前电量:{"◇" * i} {i}%', end='') # 外面用单引号,里边不可用单引号
print('电量已充满')
return 100
# charge(2)
def outer(func):
# func =inside # 将函数的内存地址作为参数传,例如这里只能是inside
def wrapper(*args, **kwargs): # 无论给wrapper函数传多少个参数,均可成功传参
start = time.time()
response = func(*args, **kwargs) # response 接返回值
end = time.time()
print(end - start)
return response
return wrapper # 这里加(),就是直接调用wrapper,得到的是wrapper的返回值;
# 不加(),得到的是wrapper的内存地址,全局就可以访问wrapper
# wrapper的内存地址就是outer的返回值,也就是res
charge = outer(charge) # 通过传参的方式,res也可换成inside
print(charge)
charge(10)
四、语法糖-代码更简洁
@装饰器函数
def outer(func):
# func =inside # 将函数的内存地址作为参数传,例如这里只能是inside
def wrapper(*args, **kwargs): # 无论给wrapper函数传多少个参数,均可成功传参
start = time.time()
response = func(*args, **kwargs) # response 接返回值
end = time.time()
print(end - start)
return response
return wrapper # 这里加(),就是直接调用wrapper,得到的是wrapper的返回值;
# 不加(),得到的是wrapper的内存地址,全局就可以访问wrapper
@outer # 只要运行到这行代码,就相当于执行 charge= outer(charge)
def charge(num):
for i in range(num, 101):
time.sleep(0.05)
# 制作进度条,字符串可以做乘法计算,一个字符串X10 就是10个字符串
print(f'\r当前电量:{"◇" * i} {i}%', end='') # 外面用单引号,里边不可用单引号
print('电量已充满')
return 100
五、无参装饰器模版
def outer(func):
def wrapper(*args, **kwargs): # 装饰器
response = func(*args, **kwargs) # 调用原函数
return response
return wrapper # 返回wrapper内存地址,调用wrapper
@outer
def func(): # 原函数
pass
1、点击pycharm的file,再点击settings,再settings界面点击ELive Templates;
2、按下图操作
3、效果图,按回车键,就自动生成装饰器模版代码
六、有参装饰器
print(home,--name--) # home 是一个函数,--name--是读函数名字的属性
#运用闭包函数
def g_outer(x, y):
def outer(func):
def wrapper(*args, **kwargs):
response = func(*args, **kwargs)
print(x, y)
return response
return wrapper
return outer
@g_outer('x','y')
def resource():
print('这是源代码')
resource()
七、有参装饰器实际应用
def g_outer(source):
def outer(func):
def wapper(*args, **kwargs):
name = input("请输入账号》》》")
password = input("请输入密码》》》")
if source == 'file':
print("基于文件的登录验证")
if name == 'jack' and password == '123':
res = func(*args, **kwargs)
return res
else:
print('账号或密码错误!')
elif source == 'sql':
print("基于sql的登录验证")
elif source == 'ldap':
print("基于ldap的登录验证")
else:
print('不支持此类登录验证')
return wapper
return outer
@g_outer('file')
def home():
print('welcome')
@g_outer('sql')
def index():
pass
@g_outer('ldap')
def defalut():
pass
home()
index()
defalut()
运行结果:
请输入账号》》》jack
请输入密码》》》12
基于文件的登录验证
账号或密码错误!
请输入账号》》》jack
请输入密码》》》123
基于sql的登录验证
请输入账号》》》jack
请输入密码》》》123
基于ldap的登录验证
八、装饰器叠加
类似于堆栈
def outer1(func):
def wapper(*args, **kwargs):
print('执行outer1')
res = func(*args, **kwargs) # 这个的func是原home的内存地址
print('outer1 执行完毕')
return res
return wapper
def outer2(func):
def wapper(*args, **kwargs):
print('执行outer2')
res = func(*args, **kwargs) # 这里的func = outer1.wrapper
print('outer2 执行完毕')
return res
return wapper
def outer3(func):
def wapper(*args, **kwargs):
print('执行outer3')
res = func(*args, **kwargs) # 这里的func = outer2.wrapper
print('outer3 执行完毕')
return res
return wapper
# 装饰器的叠加
# 装饰器的执行顺序是outer1,2,3
@outer3 # home3 =outer3(home2)
@outer2 # home2 =outer2(home1)
@outer1 # home1 =outer1(home) home1 ,outer1.wrapper
def home(z):
print('执行home功能', z)
return 'zx'
res = home(0)
print(res)
运行结果:
执行outer3
执行outer2
执行outer1
执行home功能 0
outer1 执行完毕
outer2 执行完毕
outer3 执行完毕zx