with

0 阅读3分钟

Python with 语句的用法详解

with 语句用于上下文管理,它能自动处理资源的获取和释放,确保即使发生异常也能正确清理(如关闭文件、释放锁、恢复状态等)。常见应用场景包括文件操作、数据库连接、线程锁、临时状态修改等。

1. 基本语法

with 上下文管理器 [as 变量]:
    代码块
  • 上下文管理器:支持上下文管理协议的对象(实现了 __enter____exit__ 方法)。
  • as 变量(可选):将 __enter__ 的返回值赋给变量,供代码块使用。
  • 执行流程:
    1. 调用 上下文管理器.__enter__(),返回值赋给 变量(如果有 as)。
    2. 执行代码块。
    3. 无论代码块是否发生异常,都会调用 上下文管理器.__exit__() 进行清理。

2. 最常见用法:文件操作

# 传统方式(需要手动关闭)
f = open('test.txt', 'r')
try:
    content = f.read()
finally:
    f.close()

# with 方式(自动关闭)
with open('test.txt', 'r') as f:
    content = f.read()
# 离开 with 块后,f 已被自动关闭

优点:无需显式 try/finally,代码更简洁,不会忘记释放资源。

3. 其他常见应用

(1) 线程锁(threading.Lock

import threading
lock = threading.Lock()

with lock:
    # 自动获取锁,退出时自动释放
    critical_section()

(2) 数据库连接(如 sqlite3

import sqlite3
with sqlite3.connect('db.sqlite') as conn:
    cursor = conn.cursor()
    cursor.execute('SELECT * FROM users')
    # 退出时自动提交或回滚(取决于实现)

(3) 临时修改上下文(如 decimal 上下文)

from decimal import Decimal, getcontext, localcontext

with localcontext() as ctx:
    ctx.prec = 2
    print(Decimal('1.234') + Decimal('2.345'))  # 使用临时精度
# 退出后恢复原精度

(4) 重定向标准输出(如 contextlib.redirect_stdout

import io
from contextlib import redirect_stdout

f = io.StringIO()
with redirect_stdout(f):
    print("Hello")      # 输出被捕获到 f
output = f.getvalue()

4. 同时管理多个上下文

# 同时打开两个文件
with open('input.txt') as fin, open('output.txt', 'w') as fout:
    fout.write(fin.read())

5. 自定义上下文管理器

有两种方式:(实现 __enter____exit__)或 contextlib.contextmanager 装饰器

方式一:定义类

class ManagedFile:
    def __init__(self, filename, mode='r'):
        self.filename = filename
        self.mode = mode
        self.file = None

    def __enter__(self):
        self.file = open(self.filename, self.mode)
        return self.file

    def __exit__(self, exc_type, exc_val, exc_tb):
        if self.file:
            self.file.close()
        # 返回 True 表示异常已被处理,不再向外抛出
        return False

with ManagedFile('test.txt', 'w') as f:
    f.write('Hello')

方式二:使用 @contextmanager 装饰器(生成器)

from contextlib import contextmanager

@contextmanager
def managed_file(filename, mode='r'):
    f = open(filename, mode)
    try:
        yield f          # 进入 with 块时返回,退出时继续执行
    finally:
        f.close()

with managed_file('test.txt', 'r') as f:
    content = f.read()

6. 工作原理

with 语句相当于以下 try/finally 结构:

manager = 上下文管理器
var = manager.__enter__()
try:
    代码块
finally:
    manager.__exit__(exc_type, exc_val, exc_tb)
  • __exit__ 的三个参数表示代码块中发生的异常(无异常则为 None, None, None)。
  • 如果 __exit__ 返回 True,异常会被抑制(不再向上抛出);返回 False 则继续抛出。

7. 总结

  • with 确保资源正确释放,推荐用于任何需要成对操作(打开/关闭、获取/释放)的场景。
  • 常用的内置上下文管理器:文件、锁、decimal.localcontextredirect_stdout 等。
  • 自定义上下文管理器有两种简单方式:类或 @contextmanager 生成器。
  • 多个上下文可以写在一个 with 语句中,用逗号分隔。