171 lines
5.3 KiB
Markdown
171 lines
5.3 KiB
Markdown
---
|
||
title: 资源管理
|
||
description: Python 的资源管理,以及自定义资源管理器和高级应用
|
||
keywords:
|
||
- Python
|
||
- 资源管理
|
||
- 高级应用
|
||
tags:
|
||
- Python/进阶
|
||
author: 仲平
|
||
date: 2023-11-13
|
||
---
|
||
|
||
资源管理是编程中的一个关键概念,特别是在处理文件、网络连接和数据库等资源时。Python 的 `with` 语句提供了一种高效且简便的方式来处理这些资源管理任务,确保即使在出现错误的情况下,资源也能被正确管理和释放。
|
||
|
||
## `with` 语句和上下文管理器的原理
|
||
|
||
Python 中的 `with` 语句是围绕上下文管理器概念设计的。它主要用于场景,如文件操作,以确保文件即使在出现错误的情况下也会被正确关闭。
|
||
|
||
上下文管理器是实现了特定协议的对象,该协议包含两个魔术方法:`__enter__()` 和 `__exit__()`。`__enter__()` 方法在进入 `with` 语句块前执行,而 `__exit__()` 方法则在退出 `with` 语句块时执行,即使是通过异常退出。
|
||
|
||
```python
|
||
with open('example.txt', 'w') as file:
|
||
file.write('Hello, World!')
|
||
# 文件在这里已自动关闭
|
||
```
|
||
|
||
在此示例中,`open` 函数返回的文件对象是一个上下文管理器,它保证了文件在离开 `with` 块后会自动关闭。
|
||
|
||
## 自定义资源管理
|
||
|
||
### 自定义上下文管理器的实现
|
||
|
||
创建自定义上下文管理器需要定义一个类并实现 `__enter__` 和 `__exit__` 方法。这样的类可以用来管理自定义资源,比如数据库连接。
|
||
|
||
```python
|
||
class MyDatabase:
|
||
def __enter__(self):
|
||
# 初始化数据库连接
|
||
return self
|
||
|
||
def __exit__(self, exc_type, exc_value, traceback):
|
||
# 清理资源,如关闭连接
|
||
pass
|
||
|
||
with MyDatabase() as db:
|
||
# 使用数据库连接
|
||
pass
|
||
```
|
||
|
||
### `contextlib` 模块的应用
|
||
|
||
`contextlib` 模块提供了更简洁的方法来创建上下文管理器,尤其是对于函数装饰器。使用 `contextlib.contextmanager` 装饰器,可以通过一个生成器定义上下文管理逻辑。
|
||
|
||
```python
|
||
import time
|
||
from contextlib import contextmanager
|
||
|
||
@contextmanager
|
||
def timer():
|
||
start = time.time()
|
||
yield
|
||
end = time.time()
|
||
print(f"运行时间: {end - start} 秒")
|
||
|
||
with timer():
|
||
for _ in range(1000000):
|
||
pass
|
||
```
|
||
|
||
## 高级应用
|
||
|
||
### 在并发环境中的资源管理
|
||
|
||
在 [多线程和多进程](Tech/programming-language/Python/进阶/并行和并发.md) 环境中,资源管理变得更加重要。使用线程/进程安全的上下文管理器可以确保资源在并发环境下安全地使用和释放。
|
||
|
||
```python
|
||
from threading import Thread, Lock
|
||
|
||
# 共享资源
|
||
counter = 0
|
||
# 创建一个锁对象
|
||
lock = Lock()
|
||
|
||
def increment_counter():
|
||
global counter
|
||
with lock:
|
||
# 在这个区块内,counter 变量被锁保护
|
||
for _ in range(100000):
|
||
counter += 1
|
||
|
||
# 创建线程列表
|
||
threads = []
|
||
|
||
# 创建并启动5个线程
|
||
for _ in range(5):
|
||
thread = Thread(target=increment_counter)
|
||
thread.start()
|
||
threads.append(thread)
|
||
|
||
# 等待所有线程完成
|
||
for thread in threads:
|
||
thread.join()
|
||
|
||
# 输出最终的计数器值
|
||
print(f"最终计数器值: {counter}")
|
||
```
|
||
|
||
### 管理网络
|
||
|
||
确保网络和数据库连接在使用后正确关闭是非常重要的,以避免资源泄漏。
|
||
|
||
```python
|
||
import requests
|
||
|
||
with requests.Session() as session:
|
||
response = session.get('https://example.com')
|
||
|
||
if response.status_code == 200:
|
||
print("成功获取网页内容!")
|
||
print(response.text)
|
||
else:
|
||
print("请求失败,状态码:", response.status_code)
|
||
|
||
```
|
||
|
||
### 数据库连接
|
||
|
||
假设我们使用的是 Python 的 `sqlite3` 库来管理 SQLite 数据库连接。这个示例将展示如何创建一个上下文管理器来处理数据库连接和事务。
|
||
|
||
首先,导入所需的库并设置数据库连接:
|
||
|
||
```python
|
||
import sqlite3
|
||
from contextlib import contextmanager
|
||
|
||
# 定义数据库连接函数
|
||
@contextmanager
|
||
def get_db_connection(database):
|
||
try:
|
||
# 创建数据库连接
|
||
conn = sqlite3.connect(database)
|
||
print("数据库连接成功")
|
||
yield conn
|
||
except sqlite3.Error as e:
|
||
print(f"数据库连接错误: {e}")
|
||
finally:
|
||
# 关闭数据库连接
|
||
conn.close()
|
||
print("数据库连接关闭")
|
||
|
||
if __name__ == "__main__":
|
||
# 使用上下文管理器执行数据库操作
|
||
with get_db_connection('my_database.db') as conn:
|
||
cursor = conn.cursor()
|
||
# 执行一些数据库操作
|
||
cursor.execute('''CREATE TABLE IF NOT EXISTS my_table
|
||
(id INTEGER PRIMARY KEY, name TEXT)''')
|
||
cursor.execute('''INSERT INTO my_table (name) VALUES ('Alice')''')
|
||
# 提交事务
|
||
conn.commit()
|
||
```
|
||
|
||
在这个示例中,我们首先创建了一个名为 `my_table` 的新表(如果它还不存在的话),然后向表中插入了一条记录。所有的这些操作都在安全的上下文中执行,无论操作成功还是遇到异常,数据库连接最终都会被正确关闭。
|
||
|
||
## 资源管理的最佳实践
|
||
|
||
- **及时释放资源**:使用 `with` 语句确保文件或网络连接被及时释放。
|
||
- **异常安全**:确保在资源管理器的 `__exit__` 方法中正确处理异常。
|
||
- **有效共享资源**:在多线程/多进程环境中使用安全机制共享资源。
|