AI大模型入门-Python进阶-上下文管理与with语句

视频链接:

不如语冰

https://space.bilibili.com/70431433?spm_id_from=333.1007.0.0

文章 代码链接:

https://github.com/zyf-ngu/Qmatter

7 上下文管理与with语句

上下文管理是一种用于管理资源(如文件、网络连接、锁等)的机制,它确保在代码块执行前后进行正确的资源分配和释放,即使代码块中发生了异常也是如此。 在Python中,上下文管理是通过with语句来实现的。

with语句允许我们定义一个执行上下文,在该上下文中,我们可以确保资源的获取和释放是自动完成的。 即在with语句中,我们只需要关注资源或对象本身的操作(这点和普通方法一致),系统自动释放资源和对象(这点在普通方法中需要通过try except和finally实现,即try实现功能,except处理异常,finally释放资源。)

上下文管理器的基本结构

上下文管理的基本结构由两个特殊方法组成:enter__和__exit。这两个方法定义在上下文管理器对象中。

  1. enter(self):

当进入with语句块时,会调用这个方法。该方法返回的资源(是类吗?)会被赋值给as后面的变量(如果有的话)。 通常在这个方法中获取资源(如打开文件)。

  1. exit(self, exc_type, exc_value, traceback)`:

当离开with`语句块时(无论是正常结束还是因为异常),都会调用这个方法。

参数exc_type, exc_value, traceback分别表示异常类型、异常值和调用栈。如果没有异常发生,这三个参数都是None。 如果该方法返回True,则表示异常已经被处理,不会继续向外传播;如果返回False,则异常会被重新抛出(如果没有异常,返回什么都可以)。

通常在这个方法中释放资源(如关闭文件)。

上下文管理器的种类

除了使用类定义上下文管理器,Python还提供了contextmanager装饰器(在contextlib模块中),可以快速创建基于生成器的上下文管理器。这种上下文管理器使用yield语句将函数分成两部分:yield之前的部分相当于__enter__,之后的部分相当于__exit__。

方式1:类实现(完整控制)

特点:显式定义__enter__和__exit__,拥有完整异常处理能力,支持状态管理

class FileManager:

def init(self, filename):

self.filename = filename

self.file = None

def enter(self):

print("打开文件...")

self.file = open(self.filename)

return self.file # 返回资源对象

def exit(self, exc_type, exc_val, exc_tb):

print("关闭文件...")

if self.file:

self.file.close()

处理异常(返回True则阻止异常传播)

if exc_type:

print(f"发生异常: {exc_type}")

return False # 不屏蔽异常

使用示例

with FileManager("test.txt", "w") as f:

f.write("Hello Context!") # 即使发生异常,文件也会关闭

触发异常测试: 1/0

print("操作完成")

f是返回的资源名称(可任意取),f.write是该资源自己拥有的方法。

方式2:生成器实现(简洁方案)

特点 :使用contextlib.contextmanager装饰器,用yield分隔资源获取/释放,代码更简洁

例1:

from contextlib import contextmanager

@contextmanager

def file_manager(filename, mode):

try:

print("打开文件...")

f = open(filename, mode)

yield f # 返回资源

except Exception as e:

print(f"发生异常: {type(e).name}")

finally:

print("关闭文件...")

f.close()

使用示例

with file_manager("test.txt", "a") as f:

f.write("\nAppend content")

触发异常测试: int('abc')

# 传统方式(易出错)

file = open('data.txt')

try:

data = file.read() # 若此处抛出异常,

file.close() 可能不被执行!

finally:

file.close() # 必须手动确保关闭

装饰器实现的上下文管理与普通的 try...except...finally 语句相比,核心优势在于代码复用性、可读性和扩展性,尤其更符合 "关注点分离" 的设计原则。以下从具体场景对比两者的优势:

普通 try...except 需要在每次使用资源时重复编写 "获取 - 清理" 逻辑,而装饰器实现的上下文管理可以将这部分逻辑封装成通用工具,在多个地方复用。

普通 try...except 会将 "业务逻辑" 和 "资源管理逻辑" 混在一起,而上下文管理通过 with 语句清晰分离两者,让代码意图更明确。阅读代码时,无需关注 "连接如何创建 / 关闭""异常如何处理",只需聚焦 with 块内的核心业务逻辑,降低理解成本。

普通 try...except 依赖开发者手动编写 finally 块来释放资源,容易因疏忽导致资源泄漏(如忘记写 f.close());而上下文管理通过装饰器强制封装了清理逻辑,无论开发者是否记得,资源都会被自动释放。

上下文管理通过装饰器的 finally 块(或类实现的 exit)强制执行清理操作,从机制上避免了人为遗漏,尤其适合团队协作(确保所有人都遵循资源管理规范)。

装饰器实现的上下文管理可以轻松扩展,支持嵌套、组合等复杂场景,而普通 try...except 嵌套会导致代码臃肿("金字塔问题")。

|------------|------------------|---------------------------------|
| 维度 | 装饰器上下文管理 | 普通 try...except |
| 代码复用 | 一次封装,多处复用 | 每次使用需重复编写资源管理逻辑 |
| 可读性 | 分离业务逻辑与资源管理,意图明确 | 逻辑混杂,需通读代码才能理解意图 |
| 资源安全 | 强制自动释放,避免人为失误 | 依赖手动编写 finally,易遗漏 |
| 复杂场景支持 | 简洁支持嵌套、组合等场景 | 嵌套层级深,代码臃肿 |

例2:

from contextlib import contextmanager

@contextmanagerdef session_scope():

创建新的数据库会话(相当于 enter 方法)

session = SessionLocal()

try:

将会话传递给 with 代码块使用

yield session # 暂停执行,将会话对象传递给 with 块

代码块无异常时提交事务(yield 之后的部分相当于 exit 中的正常退出逻辑)

session.commit()

except:

发生异常时回滚事务

session.rollback()

重新抛出异常

raise

finally:

始终关闭会话释放连接(相当于 exit 中的清理逻辑)

session.close()

with语句 用法

with 语句对资源的类型没有限制,只要上下文管理器的 enter 方法(或装饰器中的 yield 语句)返回的是一个合法对象,就可以作为资源变量。这个对象可以是:

基础类型(如文件对象、整数);

容器类型(如列表、字典);

可调用对象(如函数、类实例方法);

自定义类实例等。

当资源变量是函数时,它的作用通常是在 with 块内提供与当前上下文相关的操作(如示例中打印临时目录信息),同时享受上下文管理器的资源自动管理能力

with 类名(参数)/函数名(参数) as 变量:

代码块

等价于:

manager = 上下文管理器()

变量 = manager.enter()

try:

代码块

finally:

manager.exit(异常信息)

类名(参数)/函数名(参数)上面已经介绍了就是上下文管理器的2种方式,那么后面这个as 有什么作用呢?

as关键字的使用场景

|------------|------------|------------------------------|
| 情况 | 是否使用as | 说明 |
| 需要访问管理器返回值 | 必须使用 | 当 enter() 返回的值需要在代码块中使用时 |
| 仅需资源管理 | 可省略 | 不需要使用管理器返回值时 |

当需要直接操作上下文管理器返回的资源对象时:

文件操作:需要文件对象进行读写

with open('test.txt', 'w') as file: # as 接收 open() 返回的文件对象

file.write("Hello, World!") # 操作文件对象

退出 with 后文件自动关闭

当只需确保资源清理,不需要操作管理器返回值时,比如锁操作:

锁操作:只需确保锁被释放,无需直接操作锁对象

import threading

lock = threading.Lock()

with lock: # 不需要 as,因为不需要操作锁对象

print("临界区代码执行中")# 退出 with 后锁自动释放

class Timer:

def enter(self):

self.start = time.time()

def exit(self, *args):

elapsed = time.time() - self.start

print(f"代码块耗时: {elapsed:.2f}秒")

with Timer(): # 不需要 as,因为不关心返回值

time.sleep(1.5) # 模拟耗时操作

输出:

代码块耗时: 1.50秒

  1. 异常处理更健壮

问题解决:在 exit() 中可统一处理异常(如回滚事务、日志记录)。

原理exit() 接收异常信息,可通过返回 True 抑制异常。

class DatabaseTransaction:

def enter(self):

self.conn = sqlite3.connect('db.db')

return self.conn.cursor()

def exit(self, exc_type, exc_val, exc_tb):

if exc_type: # 发生异常时回滚

self.conn.rollback()

else: # 否则提交

self.conn.commit()

self.conn.close()

with DatabaseTransaction() as cursor:

cursor.execute("DELETE FROM users") # 若此处失败,自动回滚

相关推荐
科技新芯1 小时前
WAIC首个AI影视专场落地 万兴科技发布多重出海布局
人工智能·科技
日月新著1 小时前
2026年最新!五款高评Agent开源模型介绍:能自主干活的AI来了
人工智能·语言模型
柠檬味的Cat1 小时前
GEO优化系统哪个渠道商好
大数据·人工智能·python
GuWenyue1 小时前
等AI回复卡顿到劝退?Vue3+DeepSeek流式输出实战,70行代码实现打字机效果
前端·人工智能·客户端
程序员天天困1 小时前
Java 项目用 Single-flight 一招终结 AI 接口重复调用
redis·后端
心易行者1 小时前
电商卖家必备!一张图讲透AI局部重绘怎么让你省下80%的修图时间
人工智能
青春不败 177-3266-05202 小时前
基于Python实现的深度学习技术在水文水质领域应用
python·深度学习·机器学习·水文水资源·水质模型
中微极客2 小时前
从Prompt Engineering到RAG:LLM应用开发实战与性能优化全解析
人工智能·prompt