12..装饰器
def wrapper(f):
def inner(*args,**kwargs):
'被装饰之前的函数'
1.开发封闭原则
软件面世时,不可能把所有的功能都设计好,当前的未来一两年功能给你上线,定期更新迭代。对于软件之前的写的源代码一般都不会修改,对函数里面的代码以及函数的调用方式。
开放原则:在源码不改变的情况下,增加一些额外的功能。
封闭原则:不要改变源码
python中装饰器:完美的诠释了的开放封闭原则。
装饰器就是一个函数: 他要装饰一个函数,在不改变原函数的源码以及调用方式的前提下,给其增加一个额外的功能。
27.1 初识装饰器
1 李业,在一家xx科技有限公司工作,主管安排了一个任务,写一个代码测试怼怼哥写的函数的执行效率
import time
def index():
time.sleep(2)
print('欢迎访问博客园首页')
print(time.time())
start_time = time.time()
index()
end_time = time.time()
print(f'此函数的执行效率{end_time-start_time}')
2. 主管让你测试小邓,李大象,重复代码太多。
def func1():
time.sleep(2)
print('欢迎访问日记首页')
def func2():
time.sleep(1)
print('欢迎访问评论首页')
start_time = time.time()
func1()
end_time = time.time()
print(f'此函数的执行效率{end_time-start_time}')
start_time = time.time()
func2()
end_time = time.time()
print(f'此函数的执行效率{end_time-start_time}')
3. 整合到函数中
def func1():
time.sleep(2)
print('欢迎访问日记首页')
def func2():
time.sleep(1)
print('欢迎访问评论首页')
def test_time(x):
start_time = time.time()
x()
end_time = time.time()
print(f'此函数的执行效率{end_time-start_time}')
test_time(func1)
test_time(func2)
4. 怼怼哥这个函数在实际项目中被500执行,主管要求:在被执行此函数时,
同时要测试一下被执行函数的效率。
def index():
time.sleep(2)
print('欢迎访问博客园首页')
# index()
def test_time(x):
start_time = time.time()
x()
end_time = time.time()
print(f'此函数的执行效率{end_time-start_time}')
test_time(index)
版本4的问题: 开放原则满足了,封闭原则:不改变原函数的源码,以及调用方式。
违反了封闭原则:改变了函数的调用方式。
版本5: 不能改变原函数的调用方式(闭包):
def index():
time.sleep(2)
print('欢迎访问博客园首页')
index()
def func1():
time.sleep(2)
print('欢迎访问日记首页')
def test_time(x): # x = index
def inner():
start_time = time.time()
x()
end_time = time.time()
print(f'此函数的执行效率{end_time-start_time}')
return inner
index = test_time(index)
index()
语法糖@加上装饰器函数的名
def test_time(x): # x = index
def inner():
start_time = time.time()
x()
end_time = time.time()
print(f'此函数的执行效率{end_time-start_time}')
return inner
# @test_time # index = test_time(index)
def index():
time.sleep(2)
print('欢迎访问博客园首页')
index = test_time(index)
index()
def func1():
time.sleep(2)
print('欢迎访问日记首页')
@test_time
def func2():
time.sleep(1)
print('欢迎访问评论首页')
func2 = test_time(func2)
func3 = test_time(func3)
func2()
z |
27.2 被装饰函数带返回值
def test_time(x): # x = index
def inner():
start_time = time.time()
ret = x()
# print(F'ret: {ret}')
end_time = time.time()
print(f'此函数的执行效率{end_time-start_time}')
return ret
return inner
@test_time # index = test_time(index)
def index():
time.sleep(0.5)
print('欢迎访问博客园首页')
return True
print(index()) # inner()
27.3 被装饰函数带参数
无论加不加装饰器,你的实参'太白金星'应该传给形参n,。
但版本6不能实现传参,index('太白金星') == inner('太白金星')
import time
struct_time = time.localtime()
# print(time.strftime("%Y-%m-%d %H:%M:%S",struct_time))
def test_time(x):# x = index
def inner(*args,**kwargs):
#函数的定义:* **聚合
# args = ('苹果')
# args =(1,3)
start_time = time.time()
ret = x(*args,**kwargs)
#函数的执行:* **打散
# ret = x(*('苹果')) ==x('苹果',)
# ret = x(*(1,3)) == x(1,3)
#print(f'ret:{ret}')
end_time = time.time()
print(f'此函数的效率{end_time-start_time}')
return ret
return inner
@test_time # index = test_time(index)
def index(n):
time.sleep(0.5)
print(f'欢迎{n}访问博客园首页')
return True
@test_time # index = test_time(index)
def func2(a,b):
time.sleep(0.5)
print(f'最终结果:{a+b}')
return a + b
print(index('苹果')) # inner('苹果')
print(func2(1,3)) # == inner(1,3)
27.4 标准版装饰器
def warpper(f):
def inner(*args,**kwargs):
'''被装饰函数之前的操作'''
# print(657)
ret = f(*args,**kwargs)
'''被装饰函数之后的操作'''
# print('执行完毕了')
return ret
return inner
@warpper
def func():
print(111)
func()
装饰器的应用:在不改变原函数的源码以及调用方式前提下,为其增加额外的功能。登陆认证,打印日志等。
def wrapper(f):
def inner(*args,**kwargs):
ret = f(*args,**kwargs)
return ret
return inner
27.5 模拟博客园登录
dic_status = {
'username':None,
'status':False
}
#方法一:
# def get_pwd():
# dic = {}
# with open('qq',encoding='utf-8')as f1:
# for i in f1:
# line = i.strip().split("|")
# dic[line[0]] = line[1]
# return dic
# 方法二:
def get_pwd():
dic = {}
with open('qq',encoding='utf-8')as f1:
for i in f1:
user,pwd = i.strip().split("|")
dic[user] = pwd
return dic
# 方法三:
# def get_pwd():
# with open('qq', encoding='utf-8')as f1:
# return {i.strip().split('|')[0]:i.strip().split('|')[1] for i in f1}#字典推导式
def login(dic_pwd):
dic1 = dic_pwd()
count = 0
while count < 3:
username = input('请输入用户名').strip()
password = input('请输入密码').strip()
# if username in dic1 and password == dic1[username]:
if dic1.get(username) == password:
print('登录成功')
dic_status['username'] = username
dic_status['status'] = True
return True
else:
print('用户名或密码错误')
count += 1
return False
def auth(f):
def inner(*args,**kwargs):
'''要进行登陆认证:
有两个分支:
1,如果之前登陆成功了,直接访问。
2,如果这是之前没有登陆过,先进行三次登陆。
'''
if dic_status['status']:
ret = f(*args,**kwargs)
return ret
else:
if login(get_pwd):
ret = f(*args, **kwargs)
return ret
return inner
@auth
def article():
print('欢迎访问文章页面')
@auth
def diary():
print('欢迎访问日记页面')
@auth
def comment():
print('欢迎访问评论页面')
article()
diary()
comment()
27.6 装饰器的执行步骤
def wrapper(f): # 1,执行wrapper函数,并且将func函数名 传给 变量f
def inner(*args,**kwargs):
print(111) # 5,打印111
ret = f(*args,**kwargs) # 6,执行真正的func函数
print(222) # 8, 打印222
return ret
return inner
@wrapper # func = wrapper(func) # 2,将inner函数名给了重新定义的变量 func = inner
def func():
print(333) # 7,打印333
print(444) # 3, 打印 444
func() # 4,inner() 执行inner函数
print(555) # 9,打印555
def func():
print(666)
func = inner
print(func)
27.7 带参数的装饰器
def wrapper(f):
def inner(*args,**kwargs):
if f.__name__ == 'qq': #通过函数名
ret = f(*args,**kwargs)
return ret
else:
pass
return inner
def wrapper_out(n,*args,sex='男',):
def wrapper(f): # f
def inner(*args,**kwargs):
ret = f(*args,**kwargs) # func1()
return ret
return inner
return wrapper
def func1():
print('in func1')
func = wrapper_out(1) # wrapper函数名
ly = func(func1) # wrapper(func1) = inner
ly() # inner()
从两个文件读取账号密码,登录
方法一:代码多,重复
def wrapper_out(n):
def wrapper(f):
def inner(*args,**kwargs):
if n == 'qq':
username = input('请输入用户名:').strip()
password = input('请输入密码:').strip()
with open('qq',encoding='utf-8') as f1:
for line in f1:
user,pwd = line.strip().split('|')
if username == user and password == pwd:
print('登陆成功')
ret = f(*args,**kwargs)
return ret
return False
elif n == 'tiktok':
username = input('请输入用户名:').strip()
password = input('请输入密码:').strip()
with open('tiktok', encoding='utf-8') as f1:
for line in f1:
user, pwd = line.strip().split('|')
if username == user and password == pwd:
print('登陆成功')
ret = f(*args, **kwargs)
return ret
return False
return inner
return wrapper
@wrapper_out('qq')
def qq():
print('成功访问qq')
@wrapper_out('tiktok')
def tiktok():
print('成功访问抖音')
qq()
tiktok()
方法二:直接判断n 传进来的路径。
def wrapper_out(n):
def wrapper(f):
def inner(*args,**kwargs):
username = input('请输入用户名:').strip()
password = input('请输入密码:').strip()
with open(n,encoding='utf-8') as f1:
for line in f1:
user,pwd = line.strip().split('|')
if username == user and password == pwd:
print('登陆成功')
ret = f(*args,**kwargs)
return ret
return False
return inner
return wrapper
@wrapper_out('qq')
def qq():
print('成功访问qq')
@wrapper_out('tiktok')
def tiktok():
print('成功访问抖音')
qq()
tiktok()
@wrapper_out('qq')
def qq():
print('成功访问qq')
qq()
看到带参数的装饰器分两步执行:
'''
@wrapper_out('腾讯')
1. 执行wrapper_out('腾讯') 这个函数,把相应的参数'腾讯' 传给 n,并且得到返回值 wrapper函数名。
2. 将@与wrapper结合,得到我们之前熟悉的标准版的装饰器按照装饰器的执行流程执行。
'''
"""
方法三:qq 抖音两个文件取账号密码,带参数的装饰器
status = {'flag':False,'username':None}
def get_pwd(path):
dic = {}
with open(path, encoding='utf-8')as f1:
for i in f1:
line = i.strip().split('|')
dic[line[0]] = line[1]
return dic
def login(dic_pwd):
dic1 = dic_pwd
username = input('请输入用户名:')
pwd = input('请输入密码:')
if username in dic1 and dic1[username]==pwd:
print('登录成功')
status['flag'] = True
status['username'] = username
return True
else:
return False
def wrapper_out(n):
def wrapper(f):
def inner(*args,**kwargs):
if login(get_pwd(n)):
ret = f(*args,**kwargs)
return ret
return inner
return wrapper
@wrapper_out('qq')
def qq():
print('成功访问qq')
@wrapper_out('douyin')
def ti():
print('成功访问抖音')
qq()
ti()
27.8 多个装饰器装饰一个函数
def wrapper1(func1): # func1 = f原函数
def inner1():
print('wrapper1 ,before func') # 2
func1()
print('wrapper1 ,after func') # 4
return inner1
def wrapper2(func2): # func2 == inner1
def inner2():
print('wrapper2 ,before func') # 1
func2() # inner1
print('wrapper2 ,after func') # 5
return inner2
@wrapper2 # f = wrapper2(f) 里面的f == inner1 外面的f == inner2
@wrapper1 # f = wrapper1(f) 里面的f == func1 外面的 f == inner1
def f():
print('in f') # 3
f() # inner2()
def wrapper1(func):
def inner1():
print('wrapper1 ,before func')
ret = func()
print('wrapper1 ,after func')
return ret
return inner1
def wrapper2(func):
def inner2():
print('wrapper2 ,before func')
ret = func()
print('wrapper2 ,after func')
return ret
return inner2
def wrapper3(func):
def inner3():
print('wrapper3 ,before func')
ret = func()
print('wrapper3 ,after func')
return ret
return inner3
@wrapper3
@wrapper2
@wrapper1
def f():
print('in f')
return '哈哈哈'
print(f())