12 知识点总结 装饰器进阶
⼀. 通⽤装饰器的回顾
1.开闭原则: 对增加功能开放. 对修改代码封闭
2.装饰器的作⽤: 在不改变原有代码的基础上给⼀个函数增加功能
3.通⽤装饰器的写法:
def wrapper(fn):
def inner(*args,**kwargs):
print("目标函数前一行")
ret=fn(*args,**kwargs)
print("目标函数后一行")
return ret
return inner
@wrapper
def target_func(*args,**kwargs):
print("我是目标函数体")
target_func()
4.执⾏过程:
(1) 程序从上向下, 当执⾏到@wrapper的时候. 把函数作为参数传递给wrapper函数. 得到inner函数. 重新赋值给target_func
(2) 当执⾏到target_func的时候. 我们实际上执⾏的是inner函数. inner函数会先执⾏⽬标函数之前的代码. 然后再执⾏你的⽬标函数. 执⾏完⽬标函数最后执⾏的是⽬标函数
之后的代码
⼆. 函数的有⽤信息
1. 如何给函数添加注释 用三个引号来表示
def eat(food,drink):
""" eat:把传递进来的吃掉
:param food: 参数food是什么意思
:param drink: 参数drink是什么意思
:return: None 返回什么
"""
print(food,drink)
return "very good"
按住ctrl 点内置函数名,可以查看函数的注释如int,str 等
2.如何获取到函数的相关信息
def eat(food,drink):
"""
:param food: 参数food是什么意思
:param drink: 参数drink是什么意思
:return: None 返回什么
"""
print(food,drink)
# print(eat.__name__) 读取不出来
# print(eat.__doc__) 读取不出来
return "very good"
eat("水果","可乐")
print(eat.__name__) # 查询函数名
print(eat.__doc__) #查询函数文档注释 函数名.__name__可以查看函数的名字 (双下划线)
函数名.__doc__ 可以查看函数的⽂档注释
(1) 一个被装饰器装饰过的函数:查询目标函数的函数名
def wrapper(fn):
def inner(*args,**kwargs): # 聚合
print("目标函数前一行")
ret=fn(*args,**kwargs) # 打散 这⾥的作⽤. 其实就是为了保证我可以装饰所有函数⽽准备的
print("目标函数后一行")
return ret
return inner
@wrapper
def target_func(*args,**kwargs):
print("我是目标函数体")
target_func()
print(target_func.__name__) # 被装饰过的函数函数名是inner.
(2) 把上述查询函数名修改为原函数名
from functools import wraps # 加 引入函数模块
def wrapper(fn):
@wraps(fn) # 加 使用函数原来的名字
def inner(*args,**kwargs):
print("目标函数前一行")
ret=fn(*args,**kwargs)
print("目标函数后一行")
return ret
return inner
@wrapper
def target_func(*args,**kwargs):
print("我是目标函数体")
target_func()
print(target_func.__name__) # 查询结果不再是inner 而是target_func
@wrapper
def new_target_func():
print("我是另⼀个⽬标函数")
new_target_func()
print(new_target_func.__name__)
三.装饰器传参
def wrapper_out(flag):
def wrapper(fn):
def inner(*args,**kwargs):
if flag==True: # 设定条件,满足执行下一步
print("目标函数前一行")
ret=fn(*args,**kwargs)
print("目标函数后一行")
else: # 不满足执行这一步
ret=fn(*args,**kwargs)
return ret
return inner
return wrapper
@wrapper_out(True)
def target_func():
print("我是目标函数体")
target_func() # 目标函数前一行,我是目标函数体,目标函数后一行
@wrapper_out(False)
def target_func():
print("我是目标函数体")
target_func() # 我是目标函数体
执行步骤: 先执⾏wrapper(True) 然后再@返回值. 返回值恰好是wrapper. 结果就是@wrapper
四.多个装饰器装饰一个函数
def wrapper(fn):
def inner(*args,**kwargs):
print("我是a")
ret=fn(*args,**kwargs)
print("我是b")
return ret
return inner
def wrapper1(fn):
def inner(*args,**kwargs):
print("我是c")
ret=fn(*args,**kwargs)
print("我是d")
return ret
return inner
def wrapper2(fn):
def inner(*args,**kwargs):
print("我是e")
ret=fn(*args,**kwargs)
print("我是f")
return ret
return inner
@wrapper2
@wrapper1
@wrapper
def eat(*args,**kwargs):
print("我是目标函数")
eat()
执行步骤:
执⾏顺序:
⾸先@wrapper1装饰起来. 然后获取到⼀个新函数是wrapper1中的inner.
然后执⾏@wrapper2.这个时候. wrapper2装饰的就是wrapper1中的inner. 第⼆层装饰器前(第⼀层装饰器前(⽬标)第⼀层装饰器后)第⼆层装饰器后. 程序从左到右执⾏
五.补充知识点
1枚举 (同时拿元素和索引)
lst=["金","木","水","火","土"]
for index,element in enumerate(lst):
print(index,element) # 解构
for element in enumerate(lst):
print(element) # 结果是元组(0,"金)
2.存盘 , 网络传输
s="提前祝大家端午节快乐"
bys=s.encode("UTF-8") # 编码
print(bys) # 结果 字节
s1=bys.decode("UTF-8") # 解码
print(s1) # 结果 字符串
应用: 把UTF-8 转成 GBK
bys=b'\xe6\x8f\x90' # 一个字符的utf-8 形式
s=bys.decode("UTF-8") # 用UTF-8 解码成 字符串s
gbk=s.encode("GBK") # 再把字符串转化成GBK (两个字节)
print(gbk) # 结果 b'\xcc\xe1'
作业:
1.给每个函数写一个记录日志的功能,功能要求:每一次调用函数之前,要将函数名称,时间节点记录到log的日志中
所需模块
import time
struct_time = time.localtime()
# localtime() : localtime是 把从1970-1-1零点零分到当前时间系统所偏移的秒数时间转换为本地时间
print(time.strftime("%Y-%m-%d %H:%M:%S",struct_time))
# strftime是一种计算机函数,根据区域设置格式化本地时间/日期,函数的功能将时间格式化,或者说格式化一个时间字符串
import time
struct_time = time.localtime()
print(time.strftime("%Y-%m-%d %H:%M:%S",struct_time))
import time
def wrapper(fn):
def inner(*args,**kwargs):
struct_time = time.localtime() #时间
f=open("log",mode="a",encoding="UTF-8") # 文件
f.write(fn.__name__+"\t"+time.strftime("%Y-%m-%d %H:%M:%S", struct_time)+"\n") #文件名和和格式化后的时间添加到文件中 ret=fn(*args,**kwargs)# 目标函数
return ret
return inner
@wrapper
def func1(*args,**kwargs):
print("函数一")
func1()
2.京东,淘宝登陆系统
# 1,用户有两套账号密码,一套为京东账号密码,一套为淘宝账号密码分别保存在两个文件中。
# 2,设置四个函数,分别代表 京东首页,京东超市,淘宝首页,淘宝超市。
# 3,启动程序后,呈现用户的选项为:
# 1,京东首页
# 2,京东超市
# 3,淘宝首页
# 4,淘宝超市
# 5,退出程序
# 4,四个函数都加上认证功能,用户可任意选择,用户选择京东超市或者京东首页,只要输入一次京东账号和密码并成功,则这两个函数都可以任意访问;
# 用户选择淘宝超市或者淘宝首页,只要输入一次淘宝账号和密码并成功,则这两个函数都可以任意访问. flag1 = True
flag2 = True def wrapper_out(aaa):
def wrapper(fn):
def inner(*args,**kwargs):
flag = True
global flag1,flag2
if aaa=="jd" :
while flag and flag1==True:
user=input("请输入您的用户名:")
pwd=input("请输入您的密码:")
with open("jd-user",mode="r",encoding='utf-8')as f:
for i in f.readlines():
username,password=i.strip().split(",")
if user==username and pwd==password:
print("登陆成功")
flag = False
flag1 = False
break
else:
print("登陆失败")
elif aaa=="tb":
while flag and flag2==True:
user2=input("请输入您的用户名:")
pwd2=input("请输入您的密码:")
with open("tb_user",mode="r",encoding="utf-8")as f:
for i in f.readlines():
username2,password2=i.strip().split(",")
if user2==username2 and pwd2==password2:
print("登陆成功")
flag=False
flag2=False
break
else:
print("登陆失败")
ret=fn()
return ret return inner
return wrapper @wrapper_out("jd")
def jd_index():
print("京东首页") @wrapper_out("jd")
def jd_market():
print("京东超市") @wrapper_out("tb")
def tb_index():
print("淘宝首页") @wrapper_out("tb")
def tb_market():
print("淘宝超市") def logout():
print("退出程序")
global flag1, flag2
flag1=False
flag2=False def start():
with open("菜单",mode="r",encoding="utf-8")as f:
for line in f:
print(line.strip()) def choic():
while 1:
choose=input("请输入序号;")
if choose=="1":
jd_index()
elif choose=="2":
jd_market()
elif choose=="3":
tb_index()
elif choose=="4":
tb_market()
elif choose=="5":
logout()
flag=False start()
choic()
3.明星投票系统:
# 1. ⽤户注册. 将⽤户名和密码保存在⽂件中.
# 2. 登录功能. 不需要三次登录. 正常反复⽆限登录就可以. 直到登录成功为⽌
# 3. ⽤户登录后可以查看所有明星信息. 明星信息保存在明星⽂件中.
# 例如:
# 1 林俊杰 1
# 2 周杰伦 0
# 3 ⿇花藤 0
# 第⼀位是序号, 第⼆位是名字, 第三位是得票数
# 4. 给明星进⾏投票
# 5. 每个⼈每24⼩时只能投⼀次票(扩展功能, 装饰器)
# 思路: 每次⽤户投票之后. 都从⽂件中查询⼀下⽤户上⼀次投票时间. 让⽤户上⼀
# 次投票时间和当前系统时间做时间差. 如果⼤于24⼩时. 则可以进⾏投票. 如果⼩于24⼩时,
# 则提⽰⽤户不能进⾏投票. 时间处理请⾃⾏百度.
# 使⽤知识点:
# 1. 函数, 请将以上功能尽可能的封装成函数. ⽤函数来描述每⼀个功能是我们以后开发
# 经常使⽤的⼀种写法和思路
import os
import time
def wrapper(fn):
def inner():
t = time.time()
t2 = os.path.getmtime("stra_info")
if t - t2 <86400.0:
print('24小时内不要重复登陆')
return
ret=fn()
return ret
return inner def start():
with open("main",mode="r",encoding="utf-8")as f:
for line in f.readlines():
print(line.strip())
start() def regist():
with open("user_info",mode="r+",encoding="utf-8")as f1:
for i in f1.readlines():
user=input("请输入用户名:")
pad=input("请输入密码:")
if user==i.strip().split(",")[0]:
print("该用户名已存在")
else:
with open("user_info",mode="a",encoding="utf-8")as f2:
f2.write("\n"+user+","+pad)
print("注册成功")
return
# regist() def logon ():
while 1:
username1 = input("请输入用户名:")
password1 = input("请输入密码:")
with open("user_info",mode="r",encoding="utf-8")as f3:
for i in f3.readlines():
user1,pwd1=i.strip().split(',') if username1==user1 and password1==pwd1:
print("登陆成功")
return
else:
print("登陆失败请重新登陆")
# logon() def star_info():
with open("star_info", mode="r",encoding="utf-8")as f4:
for line in f4.readlines():
print(line.strip()) @wrapper
def vote():
vote_num=input('请输入明星id:')
with open("stra_info",mode="r",encoding="utf-8")as f5,open("star_info1",mode="w",encoding="utf-8")as f6:
for line in f5.readlines():
num,name,poll=line.strip().split(" ")
if vote_num==num: f6.write(num+" "+name+" "+str(int(num)+1)+'\n')
else:
f6.write(line)
os.remove("stra_info")
os.rename("star_info1","stra_info") def logout():
print('您已退出系统') def choice():
while 1:
choose=input('请输入项目序号:')
if choose=="1":
regist()
elif choose=="2":
logon()
elif choose=="3":
star_info()
elif choose=="4":
vote()
elif choose=="5":
logout()
start()
choice()