Python 异常处理完全指南:try/except 写得好才能少踩坑

小飞兽 Python 6 次阅读 2026-07-24

Introduction

代码里遇到错误怎么办?两种思路:

  • <strong>EAFP</strong>(Easier to Ask Forgiveness than Permission):先尝试,出问题再处理——try/except

  • <strong>LBYL</strong>(Look Before You Leap):先检查再行动——if/else

Python 偏好 EAFP 风格,但很多人写的异常处理其实是"捕获一切然后忽略",这不是异常处理,是把问题藏起来。

---

基础语法

1. 基本的 try/except

try:
    result = 10 / int(user_input)
except ZeroDivisionError:
    result = 0
except ValueError:
    result = None

2. 捕获异常对象(获取错误详情)

try:
    open("/etc/passwd")
except PermissionError as e:
    print(f"权限不足: {e}")
    print(f"文件名: {e.filename}")
except OSError as e:
    print(f"系统错误: {e}")

3. else:没有异常时执行

try:
    result = int(user_input)
except ValueError:
    print("不是有效整数")
else:
    # 只有 try 块完全正常执行时才会运行
    print(f"转换成功: {result}")

4. finally:无论如何都执行

conn = None
try:
    conn = open_database()
    result = conn.query(sql)
finally:
    # 不管成功还是异常,数据库连接必须关闭
    if conn:
        conn.close()

5. 自定义异常

class ValidationError(Exception):
    """数据校验失败异常"""
    def __init__(self, field, message):
        self.field = field
        self.message = message
        super().__init__(f"{field}: {message}")

# 使用
raise ValidationError("email", "格式不正确")

---

常见反模式

❌ 反模式1:捕获所有异常然后 pass

# 错误!
try:
    do_something()
except:
    pass

这会吞掉所有异常(包括 KeyboardInterrupt、SystemExit),让程序悄悄"死掉"。

✅ 正确做法:

try:
    do_something()
except SpecificException as e:
    logger.error(f"处理失败: {e}")
    # 业务相关的 fallback 逻辑

❌ 反模式2:裸 except 太宽泛

# 错误!
except Exception:
    print("出错了")

✅ 正确做法:捕获具体异常类型。

❌ 反模式3:异常里再抛异常没链

# 错误!原始异常信息丢失
try:
    int("abc")
except ValueError:
    raise RuntimeError("转换失败")

✅ 正确做法:用 raise ... from e 保留异常链:

try:
    int("abc")
except ValueError as e:
    raise RuntimeError("转换失败") from e

---

常见问题

Q1: 什么时候用异常,什么时候用 if/else?

  • <strong>用 if/else</strong>:输入验证、简单的非黑即白判断
  • <strong>用异常</strong>:真正异常情况、调用链深层需要通知调用者

Q2: 异常会影响性能吗?

正常情况下 try 不抛异常几乎没有开销。只有在频繁 try/except 在热路径(每秒调用上万次)才需要考虑优化。

Q3: <code>with</code> 语句可以替代 finally 吗?

对于实现了 __enter__/__exit__ 的对象(如文件、数据库连接),withtry/finally 更简洁:
# 简洁写法
with open("file.txt") as f:
    data = f.read()
# 自动关闭,无需 finally

---

延伸阅读

  • [[Python 日志模块 logging]] — 异常信息怎么记日志
  • [[Python 单元测试中的异常测试]] — 用 pytest.raises 验证异常行为
  • [[Python 上下文管理器]] — with 语句背后的原理

---
有问题欢迎在评论区交流!