百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

Python 上下文管理器魔法手册:with 语句的终极艺术

itomcoil 2025-05-15 18:24 2 浏览

对话实录

小白:(抓狂)我写了f = open("data.txt"),结果忘记关闭文件了!

专家:(掏出魔法书)用 with 语句,文件自动关闭,永不泄露

上下文管理器基础三连击

1. 基本用法

# 传统写法(危险!)
f = open("data.txt")
try:
 content = f.read()
finally:
 f.close()

#with写法(推荐!)
with open("data.txt") as f:
 content = f.read() # 自动关闭文件

专家提醒:任何需要清理的资源,如文件、数据库连接、网络套接字等,都要用with语句!传统写法中,若在try块内发生异常,文件可能无法正常关闭,导致资源泄露。而with语句通过上下文管理器,无论代码块内是否发生异常,都会自动关闭文件,确保资源被正确释放。

2. 自定义上下文管理器

class Timer:
    def __enter__(self):
        self.start = time.time()
        return self
    
    def __exit__(self, *args):
        self.duration = time.time() - self.start
        print(f"耗时:{self.duration:.2f}秒")

with Timer():
    time.sleep(1)  # → 耗时:1.00秒

通过实现__enter__和__exit__方法,我们可以创建自定义的上下文管理器。__enter__方法在进入with代码块时被调用,通常用于初始化资源;__exit__方法在离开with代码块时被调用,用于清理资源。

六大实战案例

案例 1:数据库连接

import sqlite3

class DBConnection:
    def __enter__(self):
        self.conn = sqlite3.connect("test.db")
        return self.conn.cursor()
    
    def __exit__(self, type, value, traceback):
        self.conn.commit()
        self.conn.close()

with DBConnection() as cursor:
    cursor.execute("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY)")

在数据库操作中,使用上下文管理器可以确保数据库连接在使用完毕后被正确关闭,同时自动提交事务,避免数据丢失或不一致的问题。

案例 2:线程锁管理

import threading
lock = threading.Lock()
with lock: # 自动获取和释放锁
	# 执行线程安全操作
	print("临界区代码")

在多线程编程中,线程锁用于保护共享资源,防止多个线程同时访问导致数据不一致。with语句结合线程锁,在进入代码块时自动获取锁,离开时自动释放锁,确保临界区代码的线程安全性。

案例 3:临时目录

from contextlib import contextmanager
import tempfile
import shutil

@contextmanager
def temp_dir():
    dirpath = tempfile.mkdtemp()
    try:
        yield dirpath
    finally:
        shutil.rmtree(dirpath)

with temp_dir() as tmp:
    print(f"使用临时目录:{tmp}")  # 自动清理

contextlib.contextmanager装饰器提供了一种更简洁的方式来创建上下文管理器。在这个案例中,temp_dir上下文管理器创建一个临时目录,在with代码块结束后自动删除该目录,避免在磁盘上留下不必要的文件。

案例 4:文件备份

import shutil

class FileBackup:
    def __init__(self, source, target):
        self.source = source
        self.target = target

    def __enter__(self):
        shutil.copy2(self.source, self.target)
        return self

    def __exit__(self, type, value, traceback):
        pass

with FileBackup("data.txt", "data_backup.txt"):
    print("数据已备份")

在进行文件操作时,有时需要先对文件进行备份。FileBackup上下文管理器在进入with代码块时,将源文件复制到目标位置,实现文件备份功能。

案例 5:日志记录

import logging

class LoggingContext:
    def __init__(self, level):
        self.level = level

    def __enter__(self):
        self.old_level = logging.getLogger().getEffectiveLevel()
        logging.getLogger().setLevel(self.level)
        return self

    def __exit__(self, type, value, traceback):
        logging.getLogger().setLevel(self.old_level)

with LoggingContext(logging.DEBUG):
    logging.debug("这是一条调试日志")

在开发过程中,我们可能需要根据不同的场景调整日志级别。LoggingContext上下文管理器在进入with代码块时,将日志级别设置为指定值,离开时恢复原来的日志级别,方便灵活地控制日志输出。

案例 6:资源监控

import resource

class ResourceMonitor:
    def __enter__(self):
        self.start_usage = resource.getrusage(resource.RUSAGE_SELF)
        return self

    def __exit__(self, type, value, traceback):
        end_usage = resource.getrusage(resource.RUSAGE_SELF)
        print(f"CPU时间:{end_usage.ru_utime - self.start_usage.ru_utime}秒")
        print(f"内存使用:{end_usage.ru_maxrss - self.start_usage.ru_maxrss}KB")

with ResourceMonitor():
    for _ in range(1000000):
        pass

ResourceMonitor上下文管理器可以在代码块执行前后监控 CPU 时间和内存使用情况,帮助我们分析代码的性能,找出潜在的性能瓶颈。

四大血泪陷阱

忘记实现__exit__

class BadManager:
    def __enter__(self):
        print("进入")
        # 忘记实现__exit__ 

with BadManager():  报错

如果自定义上下文管理器没有实现__exit__方法,在使用with语句时会抛出AttributeError异常。确保在创建上下文管理器时,正确实现__enter__和__exit__方法。

异常处理不当

class DangerManager:
    def __exit__(self, type, val, tb):
        return True  #  吞掉所有异常

with DangerManager():
    1 / 0  # 错误被静默吞掉!

__exit__方法的返回值决定了是否抑制异常。如果返回True,异常将被抑制,导致错误信息无法被正确处理。在处理异常时,应根据实际需求合理返回,避免吞掉重要的错误信息。

资源泄漏

# 错误示范
file = open("data.txt")
content = file.read()
# 忘记file.close()
# 正确做法
with open("data.txt") as file:
	content = file.read()

在使用资源时,如果忘记手动关闭资源,可能会导致资源泄漏。使用with语句可以有效避免这种情况,确保资源在使用完毕后被正确释放。

上下文管理器嵌套错误

#错误示范
with open("a.txt") as f1:
    with open("b.txt") as f2:
        pass

# 正确做法
with open("a.txt") as f1, open("b.txt") as f2:
    pass

虽然多层嵌套的with语句可以正常工作,但使用多重上下文语法可以使代码更加简洁,同时减少缩进层级,提高代码的可读性。

专家工具箱

1. contextlib模块

from contextlib import contextmanager

@contextmanager
def tag(name):
    print(f"<{name}>")
    yield
    print(f"</{name}>")

with tag("html"):
    print("网页内容")  # → <html>网页内容</html>

contextlib.contextmanager装饰器简化了上下文管理器的创建过程。通过定义一个生成器函数,在yield语句前后分别编写进入和退出上下文的逻辑,即可创建一个上下文管理器。

2. 异步上下文

class AsyncManager:
    async def __aenter__(self):
        print("异步进入")
    
    async def __aexit__(self, *args):
        print("异步退出")

async def main():
    async with AsyncManager():
        print("执行操作")

在异步编程中,异步上下文管理器通过__aenter__和__aexit__方法实现类似的功能。async with语句用于管理异步资源,确保资源在异步操作结束后被正确清理。

小白:(献上膝盖)原来上下文管理器这么强大!

专家:(扶起小白)记住:with语句是 Python 的优雅之道!

冷知识:上下文管理器的隐藏特性

  • 可重入性:某些上下文管理器支持在同一个线程中多次进入和退出,确保资源的正确管理。
  • 上下文栈:Python 维护一个上下文栈,用于管理多个上下文管理器的嵌套调用,保证资源的清理顺序正确。
  • try - finally的关系:with语句本质上是try - finally的语法糖,通过上下文管理器简化了资源管理的代码。

相关推荐

Python 上下文管理器魔法手册:with 语句的终极艺术

对话实录小白:(抓狂)我写了f=open("data.txt"),结果忘记关闭文件了!专家:(掏出魔法书)用with语句,文件自动关闭,永不泄露!上下文管理器基础三连击1.基...

【验证码逆向专栏】某安登录流程详解与验证码逆向分析与识别

声明本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!本文章未经许可禁...

Python常用内置模块介绍——文件与系统操作详解

Python提供了多个强大的内置模块用于文件和系统操作,下面我将详细介绍最常用的几个模块及其核心功能。1.os模块-操作系统交互os模块提供了与操作系统交互的接口,包括文件/目录操作、进程管理、环...

21-Python-文件操作

在Python中,文件操作是非常重要的一部分,它允许我们读取、写入和修改文件。下面将详细讲解Python文件操作的各个方面,并给出相应的示例。1-打开文件在Python中,使用`open()`函数来打...

Python 开发工程师必会的 5 个文件操作库

在Python开发的世界里,文件操作是一项基础且高频的任务。从日常的数据处理到复杂的项目部署,熟练掌握文件操作库能让我们的开发工作事半功倍。本文和大家聊聊我眼中开发必备的5个文件操作库,它们各...

你应该知道的 50 个 Python 单行代码

使用Python总是可以轻松完成一些特定任务,这让人惊奇。一些比较繁琐的任务可以使用Python在单行代码中完成。下面是我收集的50个Python单行代码实例。1.移位词:猜字母的个...

Python10个了不起的10个库,用于文件操作、接口测试

日常接口测试中需要大量的操作文件,譬如:用户登录信息、数据库信息等等。了解下方10个文件操作库,可以快速提升在工作中的效率。os:提供了与操作系统交互的功能,包括文件和目录操作、进程管理等。示例代码参...

手把手教你开发智能备份神器,小白也能30分钟搞定!

一、你的电脑是不是也总在“重复备份”?每次备份文件夹时,是不是总觉得“好麻烦”?特别是遇到几十G的文件库,整个备份过程像在坐过山车——明明大部分文件都没改,却还要从头来一遍!今天,我用Python开发...

几行代码教你zip打包

01准备有时我们不想去手动一个个去操作,然后傻等他打包完,python依赖库zipfile很方便地帮助我们封装了解压压缩,shutil用于文件目录处理,方法类似于linux命令。1、安装pipin...

Python操作目录

获取当前工作目录获取执行命令的位置路径拼接路径拆分文件重命名删除文件复制文件遍历文件夹下的文件判断文件是否存在判断目录是否存在获取当前工作目录importsysprint(sys.path[0]...

Python 开发工程师必会的 5 个系统命令操作库

当我们需要编写自动化脚本、部署工具、监控程序时,熟练操作系统命令几乎是必备技能。今天就来聊聊我在实际项目中高频使用的5个系统命令操作库,这些可都是能让你效率翻倍的"瑞士军刀"。一...

文件备份用Python,照着复制粘贴代码就可以了

引言在日常开发和运维工作中,数据安全尤为重要,定期备份重要文件是防范数据丢失的有效手段之一。本文将详细介绍如何使用Python实现一个简单的定时备份脚本,该脚本可以按照设定周期自动将指定文件夹或文件复...

2025年必学的Python自动化办公的15个实用脚本

2025年必学的Python自动化办公的6个实用脚本及其代码示例。这些脚本涵盖了文件备份、邮件通知、网页抓取、报告生成、数据处理和团队协作等多个场景,帮助用户高效完成日常办公任务。1.自动备份文件自...

一天学一点,今天学习掌握Python:异常处理与文件操作全攻略

这一笔记记录了我学习python的异常和文件的操作,这也是针对Python异常和文件操作教程的进一步优化建议和注意事项:异常处理优化1.避免过度捕获异常o不要为了捕获异常而捕获异常,应根据实际需求...

「亲测可用」如何用python脚本批量旋转图片为任意角度?

最近在训练图片方向分类器,需要对原始图片进行批量旋转操作,那如何用python脚本实现批量旋转图片为任意角度呢?此处,以将我的头像旋转90度为例进行演示。实现图片批量旋转的python源代码如下:#-...