# 装饰器 ## 什么是装饰器 让其他函数在不需要做任何代码变动的前提下,增加额外的功能,装饰器的返回值也是一个函数对象。 装饰器的应用场景:比如插入日志,性能测试,事务处理,缓存等等场景。 ## 装饰器的形成过程 如果我想测试某个函数的执行时间 ```python import time def func1(): print('in func1') def timer(func): def inner(): start = time.time() func() print(time.time() - start) return inner func1 = timer(func1) func1() ``` 但是如果有多个函数,我都想让你测试他们的执行时间,你每次是不是都得func1 = timer(func1)?这样还是有点麻烦,因为这些函数的函数名可能是不相同,有func1,func2,graph,等等,所以更简单的方法,python给你提供了,那就是语法糖。 ```python import time def timer(func): def inner(): start = time.time() func() print(time.time() - start) return inner @timer def func1(): time.sleep(1) print('in func1') func1() ``` 装饰一个带参数的函数 ```python import time def timer(func): def inner(a): start = time.time() func(a) print(time.time() - start) return inner @timer def func1(a): time.sleep(1) print(a) func1('hello world') ``` 装饰一个带各种参数的函数 ```python import time def timer(func): def inner(*args,**kwargs): start = time.time() func(*args,**kwargs) print(time.time() - start) return inner @timer def func1(*args,**kwargs): print(*args,**kwargs) func1('hello world','abc',123,432) ``` 查看函数的相关信息,在加上装饰器后就失效了 ```python def index(): '''这是一条注释信息''' print('from index') print(index.__doc__) # 查看函数注释 print(index.__name__) # 查看函数名称 ``` 导入wraps装饰器 ```python from functools import wraps def deco(func): @wraps(func) def wrapper(*args,**kwargs): return func(*args,**kwargs) return wrapper @deco def index(): '''这是一条注释信息''' print('from index') print(index.__doc__) # 查看函数注释 print(index.__name__) # 查看函数名称 ``` ## 开放封闭原则 一句话,软件实体应该是可扩展但是不可修改的。 * 对于扩展是开放的 * 对于修改是封闭的 装饰器完美的遵循了这个开放封闭原则 ## 装饰器的主要功能和固定结构 ```python def timer(func): def inner(*args,**kwargs): '''执行函数之前要做的''' re = func(*args,**kwargs) '''执行函数之后要做的''' return re return inner # 下面是加上wraps的固定结构 from functools import wraps def timer(func): @wraps(func) def wrapper(*args,**kwargs) return func(*args,**kwargs) return wrapper ``` ## 带参数的装饰器 加上一个outer函数,可以携带一个flag的值,然后控制装饰器是否生效 ```python def outer(flag): def timer(func): def inner(*args,**kwargs): if flag: print('函数开始执行') re = func(*args,**kwargs) if flag: print('函数执行完毕') return re return inner return timer @outer(True) def func(): print('test') func() ``` ## 多个装饰器装饰一个函数 ```python def wrapper1(func): def inner(): print('第一个装饰器,在程序运行之前') func() print('第一个装饰器,在程序运行之后') return inner def wrapper2(func): def inner(): print('第二个装饰器,在程序运行之前') func() print('第二个装饰器,在程序运行之后') return inner @wrapper1 @wrapper2 def f(): print('Hello') f() ``` # 小练习 - 给前面的基于文件数据库的登录注册加上一个打游戏功能 - 使用装饰器验证用户是否登录 ```python def check_auth(func): def inner(name): if len(flag) == 1: func(name) else: print("没登录就别打游戏了!") return inner @check_auth def play_game(name): print(f"{name[0]}开始游戏") flag = [] while True: # 获取文件中保存的用户名和密码信息 dic = {} with open("db", "a+", encoding="utf-8") as f: f.seek(0) temp_data = f.readlines() for i in temp_data: data = i.strip() if len(data) == 0: continue temp_db = data.split("✨") dic[temp_db[0]] = temp_db[-1] # 登录注册的逻辑 print("登录".center(30,"=")) print("请选择:\n1.登录\n2.注册\n3.退出登录\n4.打游戏") choice = input(">>>") if choice == "1": username = input("输入用户名:") if username in dic.keys(): password = input("输入密码:") if dic[username] == password: print("\033[42;30m 登录成功! \033[0m") flag.append(username) else: print("\033[41;37m 密码错误 \033[0m") else: print("\033[41;37m用户名不存在!\033[0m") elif choice == "2": username = input("输入用户名") if username in dic.keys(): print("\033[34;41m 用户名已存在!\033[0m") continue password = input("请输入密码") dic[username] = password print("\033[42;30m 注册成功! \033[0m") elif choice == "3": flag.clear() print("退出成功!") elif choice == "4": play_game(flag) else: print("\033[41;37m输入错误!\033[0m") # 将数据写入文件 with open("db", "w+", encoding="utf-8") as f: for k,v in dic.items(): data = f"{k}✨{v}\n" f.write(data) ```