09-11-周四_16-52-32
This commit is contained in:
398
project06/01.装饰器函数.md
Normal file
398
project06/01.装饰器函数.md
Normal file
@@ -0,0 +1,398 @@
|
||||
# Python装饰器
|
||||
|
||||
在 Python 中,**装饰器**是一个非常强大的功能,它允许你在不修改函数代码的情况下,动态地修改函数或方法的行为。装饰器本质上是一个函数,它接受一个函数作为参数并返回一个新的函数。
|
||||
|
||||
简单来讲:让其他函数在不需要做任何代码变动的前提下,增加额外的功能,装饰器的返回值也是一个函数对象。
|
||||
装饰器的应用场景:比如插入日志,性能测试,事务处理,缓存等等场景。
|
||||
|
||||
## 案例切入
|
||||
|
||||
已知有一个函数`func1()`,作用是输出一句话。现在我们想要给他增加额外的功能。但是为了保障已有功能的稳定,不能更改原函数。
|
||||
|
||||
```python
|
||||
def func1():
|
||||
print("in func1")
|
||||
|
||||
# 新的需求,能够打印如下内容...
|
||||
# hello world
|
||||
# in func1
|
||||
# hello python
|
||||
```
|
||||
|
||||
可以想想应该怎么做?
|
||||
|
||||
让我们一起研究一下:
|
||||
|
||||
```python
|
||||
# 实现
|
||||
def func2(func):
|
||||
def inner():
|
||||
print("hello world")
|
||||
func()
|
||||
print("hello python")
|
||||
return inner
|
||||
|
||||
func1 = func2(func1)
|
||||
func1()
|
||||
|
||||
# Output:
|
||||
hello world
|
||||
in func1
|
||||
hello pythons
|
||||
```
|
||||
|
||||
## 装饰器形成的过程
|
||||
|
||||
如果我想**测试某个函数**的执行时间
|
||||
|
||||
```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)`?这样是比较麻烦的,而且不利于代码的可读性和后期维护。这里我们可以使用python中的一种特殊的语法结构`语法糖`,来更为简便的使用装饰器。
|
||||
|
||||
我们将上述代码修改如下:
|
||||
|
||||
```python
|
||||
import time
|
||||
|
||||
def timer(func):
|
||||
def inner():
|
||||
start = time.time()
|
||||
func()
|
||||
print(time.time() - start)
|
||||
return inner
|
||||
|
||||
@timer
|
||||
# 当我们在某个函数上方使用@my_decorator的时候,python会自动将下面定义的函数做为参数传递给my_decorator。
|
||||
# 等价于func1 = timer(func1)
|
||||
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
|
||||
def my_decorator(func):
|
||||
def wrapper(*args, **kwargs):
|
||||
# 打印传入的参数
|
||||
print(f"调用 {func.__name__} 函数,参数: {args}, 关键字参数: {kwargs}")
|
||||
|
||||
# 调用原始函数并获取结果
|
||||
result = func(*args, **kwargs)
|
||||
|
||||
# 打印返回结果
|
||||
print(f"{func.__name__} 函数返回: {result}")
|
||||
|
||||
return result
|
||||
return wrapper
|
||||
|
||||
@my_decorator
|
||||
def add(x, y):
|
||||
"""返回两个数的和"""
|
||||
return x + y
|
||||
|
||||
# 测试
|
||||
result = add(5, 3)
|
||||
print(f"最终结果: {result}")
|
||||
```
|
||||
|
||||
# wraps装饰器
|
||||
|
||||
回到我们最开始的案例
|
||||
|
||||
```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`函数,但是这里的`func1`函数还是我们最初的`func1`函数吗?
|
||||
|
||||
......
|
||||
|
||||
我们先来看一下最后的`func1`他的函数名
|
||||
|
||||
```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) # 将函数本身做为参数传递进去
|
||||
|
||||
print(func1.__name__) # 查看函数的名称
|
||||
|
||||
# Output:
|
||||
inner
|
||||
```
|
||||
|
||||
## 导入wraps装饰器
|
||||
|
||||
`wraps` 装饰器,用于帮助创建装饰器时保留被装饰函数的元数据(如名称、文档字符串等)。使用 `@wraps` 可以确保装饰后的函数看起来像原始函数,这样有助于调试和文档生成。
|
||||
|
||||
我们将上方的案例使用wraps装饰器装饰
|
||||
|
||||
```python
|
||||
from functools import wraps
|
||||
import time
|
||||
|
||||
def func1():
|
||||
print('in func1')
|
||||
|
||||
def timer(func):
|
||||
@wraps(func)
|
||||
def inner():
|
||||
start = time.time()
|
||||
func()
|
||||
print(time.time() - start)
|
||||
return inner
|
||||
|
||||
func1 = timer(func1) # 将函数本身做为参数传递进去
|
||||
|
||||
print(func1.__name__) # 查看函数的名称
|
||||
```
|
||||
|
||||
# 带参数的装饰器
|
||||
|
||||
带参数的装饰器允许你在装饰器中接受参数,从而增强装饰器的灵活性和功能性。实现带参数的装饰器通常需要使用嵌套函数。
|
||||
|
||||
我们将创建一个装饰器,它接受一个参数,用于指定**是否打印函数的执行时间**。
|
||||
|
||||
```python
|
||||
import time
|
||||
from functools import wraps
|
||||
|
||||
|
||||
def timing_decorator(print_time=True):
|
||||
def decorator(func):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
start_time = time.time() # 记录开始时间
|
||||
result = func(*args, **kwargs) # 调用原始函数
|
||||
end_time = time.time() # 记录结束时间
|
||||
|
||||
if print_time:
|
||||
execution_time = end_time - start_time
|
||||
print(f"{func.__name__} 执行时间: {execution_time:.4f}秒")
|
||||
|
||||
return result
|
||||
|
||||
return wrapper
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
@timing_decorator(print_time=True)
|
||||
def add(x, y):
|
||||
"""返回两个数的和"""
|
||||
time.sleep(1) # 模拟耗时操作
|
||||
return x + y
|
||||
|
||||
|
||||
@timing_decorator(print_time=False)
|
||||
def multiply(x, y):
|
||||
"""返回两个数的积"""
|
||||
time.sleep(1) # 模拟耗时操作
|
||||
return x * y
|
||||
|
||||
|
||||
# 测试
|
||||
result_add = add(5, 3)
|
||||
print(f"加法结果: {result_add}")
|
||||
|
||||
result_multiply = multiply(5, 3)
|
||||
print(f"乘法结果: {result_multiply}")
|
||||
|
||||
# Output:
|
||||
add 执行时间: 1.0128秒
|
||||
加法结果: 8
|
||||
乘法结果: 15
|
||||
```
|
||||
|
||||
# 多个装饰器装饰一个函数
|
||||
|
||||
可以将多个装饰器应用于同一个函数。这种情况下,装饰器会按照从内到外的顺序依次应用。
|
||||
|
||||
```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()
|
||||
|
||||
# Output:
|
||||
第一个装饰器,在程序运行之前
|
||||
第二个装饰器,在程序运行之前
|
||||
Hello
|
||||
第二个装饰器,在程序运行之后
|
||||
第一个装饰器,在程序运行之后
|
||||
```
|
||||
|
||||
`@wrapper2` 首先应用于 `f`,然后 `@wrapper1` 应用于 `wrapper2` 返回的结果。
|
||||
|
||||
当你调用 `f()` 时,实际执行的过程如下:
|
||||
|
||||
- `f` 首先被 `wrapper2` 装饰,返回一个新的函数 `inner`,这个 `inner` 函数会在调用时执行 `f`。
|
||||
- 然后,`wrapper1` 装饰了 `wrapper2` 返回的 `inner` 函数,返回了另一个 `inner` 函数。
|
||||
|
||||
最终,`f` 实际上是一个被 `wrapper1` 和 `wrapper2` 装饰过的函数。
|
||||
|
||||
## 示例:多个装饰器
|
||||
|
||||
创建两个装饰器,一个用于打印函数的执行时间,另一个用于打印调用的参数。
|
||||
|
||||
```python
|
||||
import time
|
||||
from functools import wraps
|
||||
|
||||
# 装饰器 1:打印执行时间
|
||||
def timing_decorator(func):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
start_time = time.time()
|
||||
result = func(*args, **kwargs)
|
||||
end_time = time.time()
|
||||
execution_time = end_time - start_time
|
||||
print(f"{func.__name__} 执行时间: {execution_time:.4f}秒")
|
||||
return result
|
||||
return wrapper
|
||||
|
||||
# 装饰器 2:打印函数参数
|
||||
def logging_decorator(func):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
print(f"调用 {func.__name__} 函数,参数: {args}, 关键字参数: {kwargs}")
|
||||
return func(*args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
@timing_decorator
|
||||
@logging_decorator
|
||||
def add(x, y):
|
||||
"""返回两个数的和"""
|
||||
time.sleep(1) # 模拟耗时操作
|
||||
return x + y
|
||||
|
||||
# 测试
|
||||
result = add(5, 3)
|
||||
print(f"加法结果: {result}")
|
||||
|
||||
# Output:
|
||||
调用 add 函数,参数: (5, 3), 关键字参数: {}
|
||||
add 执行时间: 1.0001秒
|
||||
加法结果: 8
|
||||
```
|
||||
|
||||
# 开放封闭原则
|
||||
|
||||
开放封闭原则(Open/Closed Principle, OCP)是面向对象设计中的一个重要原则,它是 SOLID 原则之一。这个原则的核心思想是:
|
||||
|
||||
## 定义
|
||||
|
||||
- **开放性**:软件实体(如类、模块、函数等)应该对扩展开放。这意味着你应该能够通过增加新功能来扩展这些实体,而不是修改现有的代码。
|
||||
- **封闭性**:软件实体应该对修改封闭。也就是说,应该避免直接修改已存在的代码,以防止引入错误或破坏现有功能。
|
||||
|
||||
## 目的
|
||||
|
||||
开放封闭原则的主要目的是提高代码的可维护性和可扩展性。遵循这一原则可以减少对现有代码的影响,从而降低引入新错误的风险。
|
||||
|
||||
# 装饰器的固定结构
|
||||
|
||||
```python
|
||||
def outer(func):
|
||||
def inner(*args,**kwargs):
|
||||
'''执行函数之前要做的'''
|
||||
re = func(*args,**kwargs)
|
||||
'''执行函数之后要做的'''
|
||||
return re
|
||||
return inner
|
||||
|
||||
|
||||
# 下面是加上wraps的固定结构
|
||||
from functools import wraps
|
||||
|
||||
def outer(func):
|
||||
@wraps(func)
|
||||
def inner(*args,**kwargs)
|
||||
'''执行函数之前要做的'''
|
||||
re = func(*args,**kwargs)
|
||||
'''执行函数之后要做的'''
|
||||
return re
|
||||
return inner
|
||||
```
|
||||
|
169
project06/[任务书]用户登录注册功能.md
Normal file
169
project06/[任务书]用户登录注册功能.md
Normal file
@@ -0,0 +1,169 @@
|
||||
# 用户登录注册功能
|
||||
|
||||
##### 需求
|
||||
|
||||
- 优化基于文件的用户管理系统,使用函数来编写功能
|
||||
- 加上注册的功能,在程序运行的开始,让用户选择是登录和注册
|
||||
- 如果选择的是登录功能,正常登录
|
||||
- 如果选择的是注册功能,那就确保能够按照要求进行注册
|
||||
- 不管用户选择了登录还是注册,都要在新的文件`logs`中进行记录,记录格式如下
|
||||
|
||||
```
|
||||
2024-12-06 15:15:57 开始调用注册
|
||||
2024-12-06 15:15:57 结束调用注册
|
||||
2024-12-06 15:15:57 开始调用注册
|
||||
2024-12-06 15:15:57 结束调用注册
|
||||
2024-12-06 15:15:57 开始调用登录
|
||||
2024-12-06 15:15:57 结束调用登录
|
||||
2024-12-06 15:15:57 开始调用登录
|
||||
2024-12-06 15:15:57 结束调用登录
|
||||
```
|
||||
|
||||
- 用户登录成功后,可以查看当前用户的IP地址,使用闭包获取
|
||||
|
||||
## 参考代码
|
||||
|
||||
```python
|
||||
from datetime import datetime
|
||||
from urllib.request import urlopen
|
||||
|
||||
|
||||
def logs(msg):
|
||||
# 记录到logs中
|
||||
# 日期 时间 开始调用{msg}功能
|
||||
# 日期 时间 结束调用{msg}功能
|
||||
# 获取当前的时间,并且生成格式示例:2035-01-20 23:59:23
|
||||
def warpper(func):
|
||||
def inner(*args, **kwargs):
|
||||
with open('logs', 'a') as f:
|
||||
nowtime = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
f.write(f"{nowtime} 开始调用{msg}\n")
|
||||
ret = func(*args, **kwargs)
|
||||
nowtime = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
f.write(f"{nowtime} 结束调用{msg}\n")
|
||||
return ret
|
||||
|
||||
return inner
|
||||
|
||||
return warpper
|
||||
|
||||
|
||||
def get_db():
|
||||
# 获取db文件中存储的用户信息
|
||||
with open(db_file, 'a+', encoding="utf-8") as f:
|
||||
# 移动光标到开头
|
||||
f.seek(0)
|
||||
# 逐行读取内容并且放入字典db中
|
||||
for line in f:
|
||||
# 将每一行的信息读取出来,依据|分割,临时存入temp_list中
|
||||
temp_list = line.strip().split('|')
|
||||
# 字典的key是用户名,值是密码
|
||||
db[temp_list[0]] = temp_list[1]
|
||||
|
||||
|
||||
def write_db():
|
||||
# 将更新后的用户信息写回数据库文件
|
||||
with open(db_file, "w", encoding="utf-8") as f:
|
||||
for k, v in db.items():
|
||||
data = f"{k}|{v}\n"
|
||||
f.write(data)
|
||||
|
||||
|
||||
def check_user(username):
|
||||
# 检测用户是否存在
|
||||
# 返回1,表示存在
|
||||
# 返回0,表示不存在
|
||||
if db.get(username, 0):
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
|
||||
@logs("登录")
|
||||
def login(username, password):
|
||||
# 接收用户名和密码
|
||||
# 返回1,表示登录成功
|
||||
# 返回0,表示登录失败
|
||||
if db[username] == password:
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
|
||||
@logs("注册")
|
||||
def register(username, password):
|
||||
# 接收用户名和密码
|
||||
# 返回1,表示注册成功
|
||||
db[username] = password
|
||||
write_db()
|
||||
return 1
|
||||
|
||||
|
||||
def get_ip():
|
||||
# 打印用户当前的ip地址
|
||||
content = urlopen('http://myip.ipip.net').read().decode('utf-8')
|
||||
|
||||
def inner():
|
||||
print(content)
|
||||
|
||||
return inner
|
||||
|
||||
|
||||
# 闭包函数
|
||||
get_ip = get_ip()
|
||||
|
||||
|
||||
def menu():
|
||||
while True:
|
||||
# 打印菜单,并且根据用户的输入来决定使用什么功能
|
||||
print("请选择".center(20, '='))
|
||||
print("1. 登录".center(20, ' '))
|
||||
print("2. 注册".center(20, ' '))
|
||||
print("=" * 22)
|
||||
choice = input(">>>")
|
||||
# 刷新数据库里面的信息
|
||||
get_db()
|
||||
# 根据用户选择来执行登录或者注册
|
||||
if choice == '1':
|
||||
# 登录的代码
|
||||
username = input("用户名:")
|
||||
if check_user(username):
|
||||
# 当用户名存在的时候获取密码,并且进行用户校验
|
||||
password = input("密码:")
|
||||
if login(username, password):
|
||||
print("登录成功!")
|
||||
get_ip()
|
||||
else:
|
||||
print("密码不正确")
|
||||
else:
|
||||
print("用户名不存在")
|
||||
elif choice == '2':
|
||||
# 注册的代码
|
||||
username = input("用户名:")
|
||||
if check_user(username):
|
||||
# 当用户名存在的时候
|
||||
print("用户名已存在")
|
||||
else:
|
||||
password = input("密码:")
|
||||
if register(username, password):
|
||||
# 可以正确注册的时候
|
||||
print("注册成功!")
|
||||
else:
|
||||
print("未知错误!")
|
||||
else:
|
||||
print("选择错误")
|
||||
|
||||
|
||||
# 定义数据库文件的名字
|
||||
db_file = "db"
|
||||
# 定义空字典,用于临时存放数据库内容
|
||||
db = {}
|
||||
menu()
|
||||
```
|
||||
|
||||
|
||||
|
||||
><span style="color: red; background: yellow; padding: 2px 5px; font-size: 22px;">作业6.1提交的内容</span>
|
||||
>
|
||||
>- 理解程序的运行逻辑
|
||||
>- 程序运行成功的截图,单独发送给组长
|
Reference in New Issue
Block a user