Python 异常处理与资源管理
Python 用异常处理错误,用上下文管理器管理资源。两者组合构成 Python 的”安全网”——既保证错误不被静默吞掉,也保证资源不会泄漏。
异常的概念
Python 中,异常是程序运行时发生的错误。当错误发生时,Python 创建一个异常对象;若未被处理,程序终止并显示 traceback。
try/except 捕获异常 → 程序继续运行,而非崩溃
异常层次结构
BaseException
├── SystemExit
├── KeyboardInterrupt
├── GeneratorExit
└── Exception ← 应用程序应捕获的基类
├── StopIteration
├── ArithmeticError
│ ├── ZeroDivisionError
│ ├── OverflowError
│ └── FloatingPointError
├── AssertionError
├── AttributeError
├── EOFError
├── ImportError
│ └── ModuleNotFoundError
├── LookupError
│ ├── IndexError
│ └── KeyError
├── NameError
│ └── UnboundLocalError
├── OSError
│ └── IOError
├── RuntimeError
│ ├── NotImplementedError
│ └── RecursionError
├── SyntaxError
├── SystemError
├── TypeError
├── ValueError
└── UnicodeError
核心原则:只捕获 Exception 及其子类,不要捕获 BaseException(会捕获 SystemExit 和 KeyboardInterrupt,阻止程序正常退出)。
基本异常处理
四个关键字:
| 关键字 | 时机 |
|---|---|
try | 包裹可能出错的代码 |
except | 捕获指定类型的异常 |
else | try 无异常时执行 |
finally | 无论如何都执行 |
try:
result = 10 / 0
except ZeroDivisionError:
print("不能除以零!")
else:
print("操作成功")
finally:
print("始终执行")捕获多个异常
try:
value = int(user_input)
result = 100 / value
except ValueError:
print("请输入有效数字")
except ZeroDivisionError:
print("不能为零")
except (TypeError, KeyError) as e:
print(f"意外错误:{e}")捕获所有异常(危险)
try:
do_work()
except:
# ❌ 裸 except 捕获一切,包括 SystemExit
pass应至少捕获 Exception:
except Exception as e:
logging.exception("捕获到异常")raise 与异常链
主动抛出
raise ValueError("无效参数")异常链(Python 3.0+)
在处理异常时保留原始上下文:
try:
int('abc')
except ValueError as e:
raise RuntimeError('转换失败') from eExceptionGroup (Python 3.11+)
同时处理多个并发异常:
def f():
excs = []
for i in range(3):
try:
risky_op(i)
except Exception as e:
excs.append(e)
if excs:
raise ExceptionGroup("多个任务失败", excs)
try:
f()
except* ValueError as eg:
print(f"值错误:{eg.exceptions}")
except* TypeError as eg:
print(f"类型错误:{eg.exceptions}")自定义异常
class CustomError(Exception):
def __init__(self, message, code=None):
self.message = message
self.code = code
super().__init__(message)
try:
raise CustomError("资源未找到", code=404)
except CustomError as e:
print(f"错误 {e.code}: {e.message}")常用内置异常速查
| 异常 | 触发场景 |
|---|---|
ValueError | 参数类型正确但值不合法 |
TypeError | 操作应用于错误类型 |
KeyError | 字典键不存在 |
IndexError | 序列索引越界 |
AttributeError | 对象没有该属性 |
ImportError | 导入失败 |
OSError | 操作系统错误(含文件不存在) |
RuntimeError | 通用运行时错误 |
NotImplementedError | 抽象方法未实现 |
RecursionError | 超过最大递归深度 |
warnings 模块
警告不会中断程序,适合标记废弃功能:
import warnings
def old_function():
warnings.warn("此函数将在 v2.0 移除", DeprecationWarning)
# 将警告转为异常(调试用):
warnings.simplefilter('error')异常处理最佳实践
- 精确捕获:只捕获能处理的异常类型,别用裸
except: - 在合适的层级处理:底层函数抛出,上层决定如何响应
- 不吞异常:捕获后至少 log,不要静默
pass - 不用异常做控制流:异常是”异常”情况,不是条件分支
- finally 中不 return/break:会吞掉异常(3.14+ 有 SyntaxWarning)
上下文管理器与资源管理
with 语句是 Python 管理资源的核心机制——保证无论是否发生异常,资源都会被正确释放。
基本用法
with open("file.txt", "r") as f:
for line in f:
print(line)
# 文件已自动关闭——无需 f.close()同时管理多个资源
with open("input.txt") as src, open("output.txt", "w") as dst:
dst.write(src.read())实现上下文管理器
方式一:定义 __enter__ 和 __exit__:
class ManagedFile:
def __init__(self, filename):
self.filename = filename
def __enter__(self):
self.file = open(self.filename, 'r')
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
self.file.close()
# 返回 True 可抑制异常(通常不推荐)方式二:使用 contextlib(推荐):
from contextlib import contextmanager
@contextmanager
def managed_file(filename):
f = open(filename, 'r')
try:
yield f
finally:
f.close()
with managed_file("data.txt") as f:
print(f.read())contextlib 常用工具
from contextlib import closing, suppress, redirect_stdout
# closing — 把有 close() 方法的对象变成上下文管理器
from urllib.request import urlopen
with closing(urlopen('https://example.com')) as page:
data = page.read()
# suppress — 忽略指定异常
with suppress(FileNotFoundError):
os.remove('temp.txt') # 文件不存在也不报错
# ExitStack — 动态管理多个上下文
from contextlib import ExitStack
with ExitStack() as stack:
files = [stack.enter_context(open(f)) for f in filenames]
# 所有文件在退出时自动关闭上下文管理器 vs try/finally
# try/finally — 手动管理
f = open("file.txt")
try:
data = f.read()
finally:
f.close()
# with — 上下文管理器自动管理
with open("file.txt") as f:
data = f.read()永远优先使用 with——更短、更安全、不会忘记清理。
关联
- meta: 错误处理 — 跨语言错误处理对比
- 范式: 函数式编程 — Result/Either 模式