资讯动态

Python with 语句与上下文管理讲解

发布时间:2026/8/26 19:53:32 来源:尧图企业网站定制
文章目录一、为什么需要 with二、基本语法三、上下文管理器协议四、自定义上下文管理器的两种方式1. 基于类的实现2. 基于生成器的实现推荐更简洁五、__exit__ 的三个参数六、常见实际应用场景七、contextlib 常用工具八、最佳实践with语句是 Python 中用于上下文管理Context Management的语法糖最核心的作用是确保资源被正确获取和释放即使中间发生异常也能保证清理工作被执行。一、为什么需要with传统写法打开文件fopen(test.txt,r,encodingutf-8)try:dataf.read()finally:f.close()# 必须手动关闭问题容易忘记写close()异常处理代码冗余多个资源时嵌套很深使用with后withopen(test.txt,r,encodingutf-8)asf:dataf.read()# 到这里文件已经自动关闭即使中间发生异常也会关闭二、基本语法with表达式as变量:# 使用资源...# 离开 with 代码块后资源自动清理表达式必须返回一个上下文管理器对象as 变量是可选的用来接收__enter__方法的返回值常见例子# 文件withopen(data.txt,w,encodingutf-8)asf:f.write(hello)# 多个文件Python 3.10 支持括号换行with(open(a.txt)asf1,open(b.txt)asf2):...三、上下文管理器协议一个对象只要实现了以下两个方法就可以成为上下文管理器classMyContext:def__enter__(self):进入 with 代码块时调用print(获取资源)returnself# 返回值会赋给 as 后面的变量def__exit__(self,exc_type,exc_val,exc_tb):离开 with 代码块时调用无论是否发生异常print(释放资源)# 返回 True 表示异常已被处理不再向外抛出# 返回 False 或 None 表示异常继续向外抛出returnFalse使用withMyContext()asobj:print(在 with 内部)# 如果这里发生异常__exit__ 依然会被调用执行顺序调用__enter__执行with代码块调用__exit__即使发生异常也会调用四、自定义上下文管理器的两种方式1. 基于类的实现classTimer:def__enter__(self):importtime self.starttime.perf_counter()returnselfdef__exit__(self,exc_type,exc_val,exc_tb):importtime self.elapsedtime.perf_counter()-self.startprint(f耗时{self.elapsed:.4f}秒)returnFalse# 不吞掉异常withTimer():# 做一些耗时操作sum(range(1000000))2. 基于生成器的实现推荐更简洁使用contextlib.contextmanager装饰器fromcontextlibimportcontextmanagerimporttimecontextmanagerdeftimer():starttime.perf_counter()try:yield# yield 之前的代码相当于 __enter__# yield 的值会赋给 as 后面的变量finally:elapsedtime.perf_counter()-startprint(f耗时{elapsed:.4f}秒)withtimer():sum(range(1000000))带返回值的例子contextmanagerdefopen_file(path,moder):fopen(path,mode,encodingutf-8)try:yieldffinally:f.close()withopen_file(test.txt)asf:print(f.read())五、__exit__的三个参数def__exit__(self,exc_type,exc_val,exc_tb):...参数含义无异常时exc_type异常类如ValueErrorNoneexc_val异常实例Noneexc_tbtraceback 对象None如果__exit__返回True异常会被“吞掉”返回False或None则继续向外抛出。def__exit__(self,exc_type,exc_val,exc_tb):ifexc_typeisnotNone:print(f发生异常{exc_val})returnTrue# 吞掉异常程序继续执行六、常见实际应用场景文件操作最经典数据库连接 / 事务线程锁临时修改系统状态如当前目录、环境变量计时、日志、性能监控网络连接测试中的 mock 环境示例临时切换目录fromcontextlibimportcontextmanagerimportoscontextmanagerdefchange_dir(path):old_diros.getcwd()os.chdir(path)try:yieldfinally:os.chdir(old_dir)withchange_dir(/tmp):print(os.getcwd())# /tmp# 自动恢复原来的目录七、contextlib常用工具fromcontextlibimportcontextmanager,suppress,redirect_stdout,ExitStack工具作用contextmanager用生成器快速创建上下文管理器suppress忽略指定异常redirect_stdout临时重定向标准输出ExitStack动态管理多个上下文管理器nullcontext空的上下文管理器占位用忽略异常示例fromcontextlibimportsuppresswithsuppress(FileNotFoundError):os.remove(不存在的文件.txt)# 文件不存在也不会报错八、最佳实践优先使用with管理资源文件、连接、锁等需要自定义时优先用contextmanager代码更少__exit__中尽量做好清理不要轻易吞掉异常多个资源可以用嵌套with或ExitStack不要在__enter__/__exit__里写过于复杂的业务逻辑 感谢阅读想了解更多 我的博客网站 | 记录思考分享干货 我的个人主页 | 关于我、开源项目

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价