247 lines
5.8 KiB
Markdown
247 lines
5.8 KiB
Markdown
# 装饰器
|
|
|
|
## 什么是装饰器
|
|
|
|
让其他函数在不需要做任何代码变动的前提下,增加额外的功能,装饰器的返回值也是一个函数对象。
|
|
|
|
装饰器的应用场景:比如插入日志,性能测试,事务处理,缓存等等场景。
|
|
|
|
## 装饰器的形成过程
|
|
|
|
如果我想测试某个函数的执行时间
|
|
|
|
```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)
|
|
|
|
```
|
|
|