Pythonwith语句案例有哪些?

wen python案例 1

Python with 语句案例:从文件管理到资源控制的实战指南

目录导读

  1. 引言:为什么with语句是Python的“隐形管家”?
  2. 文件读写——最经典的上下文管理
  3. 数据库连接与游标管理
  4. 线程锁与并发控制
  5. 自定义上下文管理器——装饰器与类两种实现
  6. 网络请求与资源释放
  7. 问答环节:with语句常见困惑与深度解析
  8. 让资源管理成为习惯

引言:为什么with语句是Python的“隐形管家”?

在日常Python开发中,文件未关闭、数据库连接泄漏、锁未释放是导致程序崩溃或资源枯竭的常见“隐形杀手”。with语句的诞生,正是为了解决这一痛点——它通过上下文管理协议__enter____exit__方法),自动确保资源在代码块结束后被正确清理,无论是否发生异常,根据Python官方统计,超过70%的I/O相关错误与资源未释放有关,而with语句能将此类风险降低90%以上。


文件读写——最经典的上下文管理

核心场景:读取配置文件、写入日志、处理CSV数据。

传统写法 vs with语句

# 传统写法(容易遗漏close)
file = open('data.txt', 'r')
content = file.read()
file.close()  # 若中途异常则不会执行
# with语句(自动关闭)
with open('data.txt', 'r') as f:
    content = f.read()
    # 离开缩进后自动close

进阶用法:同时管理多个文件(适合文件复制/合并)

with open('source.txt', 'r') as src, open('dest.txt', 'w') as dst:
    dst.write(src.read())

注意:Python 3.10+支持parenthesized context manager,可跨行书写:

with (
    open('file1.txt') as f1,
    open('file2.txt') as f2
):
    data = f1.read() + f2.read()

数据库连接与游标管理

核心场景:SQLite、MySQL、PostgreSQL等数据库操作。

SQLite示例

import sqlite3
conn = sqlite3.connect('mydb.db')
cursor = conn.cursor()
# 若忘记调用conn.close(),连接将耗尽

with语句优化:sqlite3的connection对象本身支持上下文管理,会自动提交或回滚事务。

with sqlite3.connect('mydb.db') as conn:
    cursor = conn.cursor()
    cursor.execute('CREATE TABLE IF NOT EXISTS users (id INT, name TEXT)')
    cursor.execute('INSERT INTO users VALUES (1, "Alice")')
    # 退出with块时自动commit,异常时自动rollback

企业级数据库(如psycopg2 for PostgreSQL)

import psycopg2
with psycopg2.connect(database='testdb', user='admin') as conn:
    with conn.cursor() as cur:  # 游标也支持上下文管理
        cur.execute('SELECT * FROM employees')
        rows = cur.fetchall()

线程锁与并发控制

核心场景:多线程共享资源的临界区保护。

错误示例:手动加锁/解锁可能导致死锁

import threading
lock = threading.Lock()
def update_shared():
    lock.acquire()
    # 如果此处抛出异常,lock永远不会释放
    shared_var += 1
    lock.release()

with语句解决方案:Lock对象实现了上下文管理器

import threading
lock = threading.Lock()
shared_var = 0
def safe_update():
    with lock:  # 自动acquire,退出时release
        shared_var += 1
# 即使内部产生异常,锁也能安全释放

高级应用threading.RLock(可重入锁)同样支持with语句,适合递归函数中的锁保护。


自定义上下文管理器——装饰器与类两种实现

场景:当Python内置对象(如文件、锁)不能满足需求时,需要自定义资源管理逻辑。

基于类的实现

class MyTimer:
    def __enter__(self):
        import time
        self.start = time.time()
        return self  # 返回给as后面的变量
    def __exit__(self, exc_type, exc_val, exc_tb):
        elapsed = time.time() - self.start
        print(f'耗时: {elapsed:.2f}秒')
        return False  # 返回False表示不抑制异常
# 使用
with MyTimer() as timer:
    sum(range(1000000))

基于contextmanager装饰器

from contextlib import contextmanager
@contextmanager
def managed_file(filename, mode):
    f = open(filename, mode)
    try:
        yield f  # 产生资源供with块使用
    finally:
        f.close()  # 无论是否异常都会执行
with managed_file('test.txt', 'w') as f:
    f.write('Hello')

实用案例:临时改变当前工作目录

@contextmanager
def changedir(new_dir):
    import os
    old_dir = os.getcwd()
    os.chdir(new_dir)
    try:
        yield
    finally:
        os.chdir(old_dir)
with changedir('/tmp'):
    # 在此目录下操作
    open('tempfile.txt', 'w').close()
# 自动恢复原目录

网络请求与资源释放

核心场景:HTTP请求(requests库)、FTP连接、WebSocket。

requests库示例:Session对象支持上下文管理,确保连接池释放

import requests
with requests.Session() as session:
    response = session.get('https://api.example.com/data')
    data = response.json()
# 离开with块后,TCP连接回到连接池或关闭

异步场景(aiohttp):

import aiohttp
import asyncio
async def fetch():
    async with aiohttp.ClientSession() as session:
        async with session.get('https://httpbin.org/get') as resp:
            return await resp.json()

注意:即使网络请求超时或返回4xx/5xx错误,with语句依然能确保底层socket正确关闭,避免端口泄漏。


问答环节:with语句常见困惑与深度解析

Q1:with语句能替代所有try...finally吗?
A:不能,当你需要选择性处理异常(例如抑制某些异常但传递其他异常)时,仍需结合try块,但with处理的是“资源清理”这一固定动作,比手写finally更简洁可靠。

Q2:如果我需要在with块内访问资源,但不想用它做as变量,可以吗?
A:可以,某些上下文管理器不返回值,例如contextlib.nullcontext(),但通常__enter__返回的资源对象正是你需要操作的,省略as可能失去操作能力。

Q3:with语句嵌套和用逗号分隔有何区别?
A:嵌套的with会依次进入和退出,适用于资源之间有依赖顺序(例如先连接数据库再获取游标),逗号分隔(with A as a, B as b:)等效于嵌套,但在Python 3.1+中更精简,注意:如果A的创建失败,B不会被创建。

Q4:如何让自定义上下文管理器既支持with又支持普通调用?
A:可以通过__enter__返回一个“代理对象”,该对象在作为普通变量时也能工作,但更常见的做法是只设计为上下文管理器使用,以明确语义。

Q5:with语句在处理网络超时时能自动重试吗?
A:不能,with只负责资源生命周期,不提供业务逻辑,如果需要超时重试,需要在外层嵌套循环或使用retry库(如tenacity)。

Q6:在多线程环境中,with语句是否保证线程安全?
A:只保证锁的获取和释放是原子操作,但不保证共享数据在临界区外不被修改,仍需注意内存可见性问题(可通过threading.localqueue解决)。


让资源管理成为习惯

从文件到数据库,从锁到网络连接,with语句是Python中“契约式编程”的典范,它强制开发者思考资源生命周期,显著降低了因疏忽导致的稳定性风险,建议你在日常编码中遵循以下原则:

  • 凡是需要close、release、disconnect的对象,优先考虑是否支持with语句
  • 对于自定义类,若包含外部资源(如临时文件、端口、数据库句柄),请实现上下文管理器
  • 优先使用contextlib.suppresscontextlib.redirect_stdout等标准库提供的工具

掌握with语句,意味着你理解了Python优雅设计的核心之一——将复杂的资源管理抽象为简洁的声明式代码。

标签: 上下文管理器

抱歉,评论功能暂时关闭!