当前位置: 首页 > news >正文

context manager

## 关于 Python 中的上下文管理器

在 Python 里写代码,有时候会遇到一些需要“善后”的场景。比如打开一个文件,用完之后得记得关掉;或者连接数据库,操作完也得断开。这些操作如果忘了,可能会出问题,比如文件被占用、数据库连接数不够用之类的。

上下文管理器就是用来处理这类问题的。它提供了一种结构化的方式,来确保某些操作在执行前后能被正确处理。最典型的用法就是with语句。

举个例子,读写文件的时候,一般会这么写:

withopen('data.txt','r')asf:content=f.read()

这里open()返回的就是一个上下文管理器。在进入with块的时候,文件被打开,并赋值给f;离开with块的时候,无论中间有没有出错,文件都会被自动关闭。这比手动写try...finally要简洁不少。

它是怎么工作的

上下文管理器背后其实有两个特殊方法:__enter____exit____enter__在进入with块时调用,返回值会赋给as后面的变量;__exit__在离开with块时调用,负责处理清理工作,比如关文件、关连接、释放锁等等。

自己实现一个上下文管理器也不难。比如模拟一个简单的“计时器”:

classTimer:def__enter__(self):importtime self.start=time.time()returnselfdef__exit__(self,exc_type,exc_val,exc_tb):importtime self.end=time.time()print(f'耗时:{self.end-self.start:.2f}秒')withTimer()ast:# 模拟一些耗时操作importtime time.sleep(1)

这样,代码块执行的时间就会被自动打印出来。

更轻便的写法

如果觉得写一个类有点重,也可以用contextlib模块里的contextmanager装饰器,通过生成器来快速实现。上面的计时器可以改写成这样:

fromcontextlibimportcontextmanagerimporttime@contextmanagerdeftimer():start=time.time()yieldend=time.time()print(f'耗时:{end-start:.2f}秒')withtimer():time.sleep(1)

这种写法更函数式,适合一些简单的场景。yield之前的部分相当于__enter__,之后的部分相当于__exit__

实际用在哪里

上下文管理器不只是用来开关文件。比如在多线程里管理锁,可以确保锁一定会被释放:

importthreading lock=threading.Lock()withlock:# 临界区代码pass

再比如临时修改某个配置,执行完再改回来:

importsysfromcontextlibimportredirect_stdoutwithopen('output.log','w')asf,redirect_stdout(f):print('这行内容会被写入文件,而不是打印到屏幕')

这里还展示了同时使用多个上下文管理器,用逗号隔开就行。

一点细节

__exit__方法会接收到三个参数,分别代表异常类型、异常值和 traceback。如果代码块正常执行,它们都是None。如果发生异常,__exit__可以选择处理异常(返回True)或者让异常继续向上抛(返回False)。这让上下文管理器不仅能做清理,还能做错误处理。

比如可以写一个忽略特定错误的上下文管理器:

classIgnoreError:def__enter__(self):passdef__exit__(self,exc_type,exc_val,exc_tb):ifexc_type==ValueError:print('忽略 ValueError')returnTruereturnFalsewithIgnoreError():raiseValueError('这个错误会被忽略')

最后

上下文管理器是 Python 里一个挺实用的特性。它把“准备”和“清理”的逻辑封装在一起,让代码更清晰,也更容易维护。平时写代码的时候,如果遇到成对出现的操作,比如打开关闭、加锁解锁、开始结束,都可以考虑用上下文管理器来组织。时间长了,代码会显得更有条理,也不容易漏掉那些关键的清理步骤。

http://www.jsqmd.com/news/583710/

相关文章:

  • Abaqus模拟铝合金搅拌摩擦焊顺序热力耦合过程:残余应力仿真与最优焊接方案对比
  • SerialToProcessing:面向Processing的嵌入式轻量串口协议
  • 2026届学术党必备的AI辅助写作工具推荐榜单
  • 《算法题讲解指南:动态规划算法--子数组系列》--21.乘积最大子数组,22.乘积为正数的最长子数组
  • Go语言的错误处理:从panic到优雅降级
  • 多层PCB内部结构与工艺全解析
  • ARM-Linux与MCU开发的核心差异解析
  • Go语言的并发编程:从Goroutine到Channel
  • C/C++堆栈工作机制与优化实践
  • JTAG接口原理与故障诊断实战指南
  • C语言断言函数:原理、应用与最佳实践
  • NuvIoT_Utils:嵌入式IoT终端轻量级硬件抽象工具库
  • 《掌控习惯》书摘3
  • 嵌入式开发入门:串口通信与Linux操作实战
  • MB85RCxx FRAM嵌入式I²C驱动设计与工程实践
  • Go语言的性能优化:从分析到实践
  • 太卷了,阿里一面试官把多年总结的Java八股文完全开源了.......
  • RP2040 PIO软件串口:高精度轻量级UART实现
  • 危包企业可靠吗?2026年江苏危险品包装安全与创新趋势深度解读 - 2026年企业推荐榜
  • 第16章 案例特训专题【数据库篇】
  • Go语言的HTTP服务器:从基础到高级
  • PING)))超声波测距模块底层驱动开发与高精度TOF测量
  • 【后端】业务逻辑与应用逻辑:构建可维护软件的关键分离
  • MicroPython for mbed经典平台移植深度解析
  • 技术突破:高效解决Cursor试用限制的完整方案
  • HarborScaleSDK:ESP32/ESP8266轻量级物联网上云SDK
  • YoupiLabESP32库:面向IoT平台的轻量级嵌入式通信框架
  • 2026年润城地址挂靠服务深度测评:五家实力服务商横向对比与选择指南 - 2026年企业推荐榜
  • 51单片机中断机制详解与实战应用
  • 极客老王说Agent:具备“看屏幕”能力的Agent如何击穿传统接口无法触达的业务荒原?