news 2026/6/16 5:17:43

Python contextlib模块高级使用技巧

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python contextlib模块高级使用技巧

Python contextlib模块高级使用技巧

contextlib提供了比with语句更丰富的上下文管理工具。@contextmanager装饰器将一个生成器函数转为上下文管理器:

from contextlib import contextmanager

@contextmanager
def managed_resource(*args, **kwargs):
resource = acquire_resource(*args, **kwargs)
try:
yield resource
finally:
release_resource(resource)

yield之前的代码对应__enter__,yield之后的对应__exit__。finally确保异常时也能清理。

contextmanager的实现原理:

class _GeneratorContextManager:
def __init__(self, func, args, kwds):
self.gen = func(*args, **kwds)
self.func, self.args, self.kwds = func, args, kwds

def __enter__(self):
try:
return next(self.gen)
except StopIteration:
raise RuntimeError("generator didn't yield")

def __exit__(self, type, value, traceback):
if type is None:
try:
next(self.gen)
except StopIteration:
return False
else:
raise RuntimeError("generator didn't stop")
else:
if value is None:
value = type()
try:
self.gen.throw(type, value, traceback)
except StopIteration as exc:
return exc is not value
except RuntimeError as exc:
if exc is value:
return False
if exc.__cause__ is value:
return False
raise
except BaseException:
if sys.exc_info()[1] is value:
return False
raise
return True

__enter__调用next启动生成器到yield。__exit__恢复生成器执行清理代码。异常通过throw传递到生成器内部。

ExitStack组合管理多个上下文管理器:

from contextlib import ExitStack

def clean_up_resources():
stack = ExitStack()
try:
file1 = stack.enter_context(open('file1.txt'))
file2 = stack.enter_context(open('file2.txt'))
db = stack.enter_context(db_connection())
return process_files(file1, file2, db)
except:
stack.close()
raise

enter_context注册上下文管理器到栈中。ExitStack退出时按注册逆序清理所有资源。

ExitStack的回调注册:

stack = ExitStack()
stack.callback(lambda: print("cleanup 1"))
stack.callback(lambda: print("cleanup 2"))
stack.close()
# 输出: cleanup 2 cleanup 1

回调按注册逆序调用。push与callback类似但接受额外的参数。

ExitStack.pop_all转移所有权:

def transaction_context():
stack = ExitStack()
stack.enter_context(start_transaction())
def commit():
if success:
stack.pop_all()
commit_transaction()
stack.close()
return stack, commit

ExitStack的enter_context的异步版本在AsyncExitStack中。

suppress忽略指定异常:

from contextlib import suppress
import os

with suppress(FileNotFoundError):
os.remove('temporary_file.txt')

等效于:

try:
os.remove('temporary_file.txt')
except FileNotFoundError:
pass

suppress的实现接受多个异常类型,在__exit__中捕获并返回True抑制。

redirect_stdout和redirect_stderr捕获输出:

from contextlib import redirect_stdout, redirect_stderr
import io

f = io.StringIO()
with redirect_stdout(f), redirect_stderr(f):
print("this goes to f")
print("this also", file=sys.stderr)
output = f.getvalue()

redirect_stdout替换sys.stdout,redirect_stderr替换sys.stderr。在with块内所有标准输出写入StringIO。

contextlib.ContextDecorator使上下文管理器可作为装饰器:

from contextlib import ContextDecorator

class notify(ContextDecorator):
def __enter__(self):
print('Starting')
return self
def __exit__(self, *exc):
print('Finishing')
return False

@notify()
def my_function():
print('Inside')

my_function()
# Starting
# Inside
# Finishing

ContextDecorator实现了__call__方法,使实例可以作为装饰器使用。decorate方法可以被重写。

contextlib.nullcontext作为不执行任何操作的上下文管理器:

from contextlib import nullcontext

def process(need_db):
ctx = db_session() if need_db else nullcontext()
with ctx as resource:
if resource:
resource.query(...)

nullcontext返回指定的值(默认为None)。在需要条件上下文管理器的场景下替代None检查。

contextlib.chdir临时改变工作目录:

from contextlib import chdir
import os

original = os.getcwd()
with chdir('/tmp'):
print(os.getcwd()) # /tmp
print(os.getcwd()) # 回到原始目录

Python 3.11+。chdir在__enter__时保存pwd,__exit__时恢复。

@contextmanager的异常处理:

@contextmanager
def swallowing():
try:
yield
except ValueError:
print("ValueError caught and swallowed")

with swallowing():
raise ValueError("problem")
print("Continues here")

yield在try块中,异常可以被捕获和处理。如果没有合适的异常处理,异常会在__exit__中重新抛出。

AsyncExitStack用于异步上下文管理器的组合:

from contextlib import AsyncExitStack

async def async_task():
async with AsyncExitStack() as stack:
conn = await stack.enter_async_context(db_connect())
file = await stack.enter_async_context(open_async('file'))
return await process(conn, file)

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/16 5:17:42

Akagi:你的终极智能麻将AI助手,轻松成为雀魂高手!

Akagi:你的终极智能麻将AI助手,轻松成为雀魂高手! 【免费下载链接】Akagi 支持雀魂、天鳳、麻雀一番街、天月麻將,能夠使用自定義的AI模型實時分析對局並給出建議,內建Mortal AI作為示例。 Supports Majsoul, Tenhou, …

作者头像 李华
网站建设 2026/6/16 5:17:42

如何高效管理华硕笔记本:轻量级控制工具G-Helper完整指南

如何高效管理华硕笔记本:轻量级控制工具G-Helper完整指南 【免费下载链接】g-helper Lightweight Armoury Crate alternative for Asus laptops with nearly the same functionality. Works with ROG Zephyrus, Flow, TUF, Strix, Scar, ProArt, Vivobook, Zenbook,…

作者头像 李华
网站建设 2026/6/16 5:14:51

S7-200 Smart PLC学习程序分享-医药洁净空调箱温湿度串级 PID 方案

分享程序说明: S7-1500 支持STRUCT结构体、DB 块优化访问,可批量打包空调箱点位;但S7-200SMART 无原生结构体数据类型,仅支持 V 区、M 区、AI/AQ、数组,点位分散,上位机 WinCC 组态工作量极大。 医药 GMP…

作者头像 李华
网站建设 2026/6/16 5:12:47

APP开发技术要点拆解:从前端到后端,逐一攻破

APP开发是一个系统性的工程,涉及前端、后端、数据库、接口等多个方面的技术,想要开发出优质的APP,就需要逐一攻破这些核心技术要点。很多开发者在开发过程中,之所以会遇到各种问题,就是因为对技术要点掌握不扎实&#…

作者头像 李华
网站建设 2026/6/16 5:07:00

AI大模型到底是什么:从认知原理到零代码落地指南

1. 这不是“高科技黑话”,而是你每天都在用的工具底层逻辑“什么是AI大模型?用来做什么?能用它做什么?”——这问题我去年在社区里被问了至少87次,提问者里有刚毕业想转行的文科生,有开了二十年小餐馆突然被…

作者头像 李华
网站建设 2026/6/16 5:02:04

被裁后我才明白:普通打工人最该考的证,不是CPA而是PMP

35岁被优化,投了上百份简历石沉大海。很多人问:“现在这大环境,普通人该怎么建立护城河?”听句劝,别瞎折腾副业了,去考个PMP(项目管理专业人士)吧。它是普通打工人性价比最高的“职场杠杆”。PMP教你用风险登记册预判职业危机,用项目生命周期规划3年、5年目标。更香的是,在很多…

作者头像 李华