飞翔飞翔
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
  • 学习路径
  • 第1章 Python简介

    • Python是什么
    • 安装与运行
    • 交互式解释器
    • 注释与编码规范
  • 第2章 变量与数据类型

    • 变量与对象
    • 整数 int
    • 浮点数 float
    • 复数 complex
    • 布尔值 bool
    • 字符串 str
    • 空值 None
    • 类型转换
  • 第3章 运算符与表达式

    • 算术运算符
    • 比较运算符
    • 赋值运算符
    • 逻辑运算符
    • 位运算符
    • 身份与成员运算符
    • 海象运算符
    • 运算符优先级
  • 第4章 流程控制

    • if 语句
    • if-else 语句
    • if-elif-else 语句
    • match-case 语句
    • 条件表达式(三元运算符)
    • while 循环
    • for 循环
    • range 函数
    • break 与 continue
    • 循环的 else 子句
    • pass 语句
  • 第5章 数据结构

    • 列表创建与索引
    • 列表方法
    • 列表推导式
    • 元组
    • 序列解包
    • 集合
    • 字典创建与访问
    • 字典方法
    • 字典推导式
    • range 对象
  • 第6章 函数

    • 定义函数
    • 位置参数与关键字参数
    • 默认参数
    • 可变参数
    • 解包实参
    • 函数返回值
    • lambda 表达式
    • 文档字符串与注解
    • 作用域与命名空间
    • global 与 nonlocal
  • 第7章 模块与包

    • 模块导入
    • 模块搜索路径
    • 包与相对导入
    • 标准库概览
  • 第8章 文件与输入输出

    • 文件读写
    • 上下文管理器
    • 字符串格式化
    • JSON 与 CSV
  • 第9章 面向对象

    • 类与对象
    • 方法
    • 实例变量与类变量
    • 私有变量
    • 继承
    • 多重继承
    • 魔术方法
    • 属性装饰器
    • 数据类 dataclass
  • 第10章 异常处理

    • 语法错误与异常
    • try-except
    • 异常链与 raise
    • 清理操作
    • 自定义异常
  • 第11章 迭代器与生成器

    • 迭代器协议
    • 生成器
    • 生成器表达式
    • 迭代工具
  • 第12章 高级特性

    • 装饰器
    • 函数式编程
  • 第13章 工程实践

    • 测试与调试
    • 代码质量
    • 虚拟环境

清理操作

程序在操作外部资源(文件、网络连接、数据库事务、锁)时,必须确保无论操作成功还是失败,资源都能被正确释放。Python 提供了 finally 子句和 with 语句两种机制来保证清理操作的执行。

finally 子句

finally 子句是 try 语句的可选组成部分,定义了在所有情况下都必须执行的清理代码。它是 try 语句结束前执行的最后一项任务。

try:
    f = open("data.txt", "r")
    data = f.read()
    print(data.upper())
except FileNotFoundError:
    print("文件不存在")
except UnicodeDecodeError:
    print("编码错误")
finally:
    print("关闭文件")
    f.close()

无论 try 中发生什么,finally 都会执行:

  • 如果 try 正常完成,finally 执行后整个语句结束。
  • 如果 try 触发异常并被 except 捕获,finally 在异常处理完成后执行。
  • 如果 try 触发异常但没有匹配的 except,finally 先执行,然后异常继续向上传播。
  • 如果 except 或 else 子句执行期间又触发新异常,finally 执行后新异常被重新抛出。
def demo(x, y):
    try:
        result = x / y
    except ZeroDivisionError:
        print("除零错误")
        return "error"
    else:
        print(f"结果:{result}")
        return result
    finally:
        print("finally 执行")

print(demo(10, 2))   # 正常路径,finally 执行,返回 5.0
print(demo(10, 0))   # 捕获异常,finally 执行,返回 "error"

finally 的一个特殊行为是:如果其中包含 return、break 或 continue,未被处理的异常将不会被重新引发。同时,如果 try 和 finally 都有 return,返回值来自 finally。

def tricky():
    try:
        return "try"
    finally:
        return "finally"

print(tricky())  # finally

这种写法容易让人困惑,实际编码中应避免在 finally 中使用 return。

with 语句与上下文管理器

with 语句提供了一种更优雅的资源管理方式。它确保在代码块结束时调用对象的清理方法,即使代码块中发生了异常。

with open("data.txt", "r") as f:
    for line in f:
        print(line.strip())
# 离开 with 块时,f.close() 自动被调用

with 语句的工作原理依赖于上下文管理器协议:对象必须实现 __enter__() 和 __exit__() 两个方法。__enter__() 在进入 with 块时执行,其返回值绑定到 as 后的变量;__exit__() 在离开 with 块时执行,负责清理工作。

class ManagedResource:
    """模拟一个需要手动释放的资源。"""
    
    def __init__(self, name):
        self.name = name
    
    def __enter__(self):
        print(f"[{self.name}] 资源初始化")
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        print(f"[{self.name}] 资源清理")
        # 返回 False 表示不吞掉异常,让它继续传播
        return False
    
    def do_work(self):
        print(f"[{self.name}] 执行操作")

with ManagedResource("数据库连接") as res:
    res.do_work()
# 输出:
# [数据库连接] 资源初始化
# [数据库连接] 执行操作
# [数据库连接] 资源清理

__exit__() 接收三个参数:异常类型、异常值和 Traceback。如果 with 块正常结束,这三个参数都是 None。如果发生了异常,它们描述该异常;__exit__() 可以选择处理异常并返回 True(异常被吞掉不再传播),或返回 False(异常继续传播)。

class SuppressZeroDivision:
    """一个会吞掉 ZeroDivisionError 的上下文管理器。"""
    
    def __enter__(self):
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type is ZeroDivisionError:
            print(f"已抑制:{exc_val}")
            return True  # 吞掉异常
        return False

with SuppressZeroDivision():
    print(1 / 0)
    print("这行不会执行")
print("with 块之后继续执行")

contextlib 模块

contextlib 提供了便捷工具,让创建上下文管理器更简单,无需编写完整的类。

contextlib.contextmanager

用装饰器将生成器函数转换为上下文管理器。yield 之前的代码相当于 __enter__(),yield 之后的代码相当于 __exit__()。

from contextlib import contextmanager

@contextmanager
def managed_file(path, mode):
    f = open(path, mode)
    try:
        yield f
    finally:
        f.close()

with managed_file("test.txt", "w") as f:
    f.write("hello")

如果 yield 和 finally 之间的代码抛出异常,异常会在 yield 处被注入到生成器中,然后执行 finally 块。这与类方式的 __exit__() 行为一致。

from contextlib import contextmanager

@contextmanager
def temp_database():
    print("创建临时数据库")
    db = {"status": "ready"}
    try:
        yield db
    except Exception as e:
        print(f"操作失败,回滚:{e}")
        db["status"] = "rolled_back"
        raise
    finally:
        print(f"清理临时数据库,最终状态:{db['status']}")

with temp_database() as db:
    print(f"使用数据库:{db}")
    # raise ValueError("模拟错误")

contextlib.closing

对于本身不是上下文管理器但具有 close() 方法的对象(如某些第三方库的资源),closing() 可以将其包装为上下文管理器。

from contextlib import closing
import urllib.request

with closing(urllib.request.urlopen("https://example.com")) as response:
    data = response.read()

contextlib.suppress

suppress() 创建一个上下文管理器,自动忽略指定的异常类型,让代码更简洁。

from contextlib import suppress
import os

# 传统写法
try:
    os.remove("maybe_not_exist.txt")
except FileNotFoundError:
    pass

# 简洁写法
with suppress(FileNotFoundError):
    os.remove("maybe_not_exist.txt")

contextlib.ExitStack

当需要同时管理多个上下文管理器,且数量在运行时才能确定时,ExitStack 提供了动态进入和退出的能力。

from contextlib import ExitStack

files = ["a.txt", "b.txt", "c.txt"]

with ExitStack() as stack:
    handles = [stack.enter_context(open(f)) for f in files]
    # 所有文件在此处同时打开
    contents = [f.read() for f in handles]
    # 离开 with 块时,所有文件按相反顺序自动关闭

ExitStack 还会正确处理多个上下文管理器中的异常:如果某个 __exit__() 抛出异常,后续上下文管理器仍会被清理。

资源管理实战模式

模式一:嵌套 with 语句

当需要管理多种资源时,可以嵌套使用 with 语句,或者利用 Python 3.1+ 支持的多项 with 语法:

# 嵌套写法
with open("input.txt") as src:
    with open("output.txt", "w") as dst:
        dst.write(src.read().upper())

# 并行写法(推荐)
with open("input.txt") as src, open("output.txt", "w") as dst:
    dst.write(src.read().upper())

模式二:可重入锁

from threading import RLock

lock = RLock()

with lock:
    print("持有锁")
    with lock:  # RLock 支持同线程重入
        print("再次持有锁")
    print("释放内层锁")
print("释放外层锁")

模式三:数据库事务

from contextlib import contextmanager

@contextmanager
def transaction(conn):
    cursor = conn.cursor()
    try:
        yield cursor
        conn.commit()
    except Exception:
        conn.rollback()
        raise

with transaction(db_conn) as cur:
    cur.execute("INSERT INTO users (name) VALUES (?)", ("Alice",))
    cur.execute("INSERT INTO logs (action) VALUES (?)", ("create_user",))
# 成功时自动 commit,异常时自动 rollback
上一页
异常链与 raise
下一页
自定义异常