Python 捕获异常怎么写?附 3 种常见写法一次讲清

编程狮(w3cschool.cn) 2026-09-03 17:35:18 浏览数 (23)
反馈

捕获异常,最基础的写法是把可能出错的代码放进 try,再用 except 接住具体异常:try 里放可能报错的语句,except 后面写要捕获的异常类型,程序就不会因为异常而中断崩溃。本文讲清 Python 捕获异常的 3 种主流写法:try-except 基础结构、捕获特定异常并配合 else/finally、以及自定义异常与 with 上下文管理。你还会看到「该捕获哪些异常」「finally 和 else 有什么区别」「什么时候该自己抛异常」这些真实开发里常卡住的问题。读完你不仅能把异常稳稳接住,还知道每种写法该用在哪个场景,写出既健壮又好维护的代码。今天编程狮就把这块讲透。

Python 捕获异常方法图

一、为什么要捕获异常

程序跑着跑着突然崩掉,十有八九是遇到了它没预料到的错误:读文件时文件不存在、把用户输入当成数字去算却发现是字母、访问网络时连接超时。这些在 Python 里都会以「异常」的形式抛出来。如果不处理,异常会一路向上冒泡,最终被解释器接住,打印一长串堆栈后直接终止进程。

捕获异常的意义,就是在某些「可预期会出错」的地方,主动把异常接住、决定怎么应对,而不是让整个程序猝死。比如读配置文件失败了,你可以给它一个默认值,让程序继续跑;或者记一条日志,再友好地告诉用户「配置有误」而不是甩出一堆英文报错。

⚠️ 注意:捕获异常不是用来掩盖 bug 的。逻辑本身写错、或者你根本没想清楚为什么这里会出错,就盲目 try-except 包起来,只会让问题藏得更深,排查时更难定位。

还有一个常见误区:很多人以为「加个 try 就安全了」。其实 try 只能接住它作用范围内、且类型对得上的异常。你写了 except ValueError,结果这里实际抛的是 KeyError,那 ValueError 这个网就兜不住,异常照样往上冒。所以捕获异常的前提,是先想清楚「这里可能抛什么」。想系统理解异常体系,可以从 Python3 基础教程 的异常章节补起。

另外,捕获异常和「防御式编程」是互补的:能提前判断的(比如先 if 列表非空再取第一个),就别等异常抛出来再接;真正不可控的(外部输入、IO、网络),才用异常兜底。分清楚这两类,代码既清晰又高效。

二、方法一:try-except 基础结构(最常用)

适合谁:刚想给某段「可能出错」的代码加保护,不确定细节,先用最朴素的形式接住。代价:如果只写裸 except,会连你不想接的异常也一起吞掉(见第五节)。

try:
    num = int(input("请输入一个整数:"))
    print("你输入的是", num)
except ValueError:
    print("输入的不是合法整数,请重新输入")

把「可能出错」的 int(input(...)) 放进 try,Python 一旦在这里抛出 ValueError,就立刻跳到 except 分支执行,而不是崩溃。int() 遇到无法转成整数的字符串时会抛 ValueError,这正是我们要接的类型。

try-except 的基本规律是:try 块里从出错那一行起,后面的语句就不再执行,直接跳转对应 except;如果 try 里一切正常,except 就整段被跳过。一个常见误用是把一大片毫不相干的代码全塞进一个 try,结果出问题了根本不知道是哪一行触发的。原则是:try 里只放「真正可能出错、且出错类型明确」的那几行。

异常类型要接得越具体越好。Python 内置了丰富的异常层级,ValueError、TypeError、KeyError、IndexError 各管一类。接得具体,意味着你清楚自己在防什么;接得宽泛,反而容易把别的 bug 也盖住。下面一节我们看怎么把「接得准」这件事做得更到位,以及在捕获特定异常时如何配合 else 和 finally 把正常逻辑与清理逻辑拆开。

三、方法二:捕获特定异常 + else / finally(最完整)

适合谁:不仅要接住异常,还想区分「正常分支」和「清理分支」。代价:结构比基础版略长,但可读性更好。

try:
    f = open("data.txt", encoding="utf-8")
    content = f.read()
except FileNotFoundError:
    print("文件不存在,使用空内容兜底")
else:
    print("读取成功,共", len(content), "字")
finally:
    f.close()
    print("无论成功失败,文件都已关闭")

这里出现了三个关键角色。except FileNotFoundError 只接「文件不存在」这一种异常,别的异常(比如权限问题 PermissionError)它不接,会继续向上冒泡——这通常是你想要的,因为权限错属于该让你知道的真问题。else 块在 try 完全没出错时才执行,专门放「依赖 try 成功结果的后续操作」,把正常逻辑和容错逻辑分开,读起来更清楚。finally 块无论成功失败都会执行,最适合放资源释放、锁释放这类「必须做」的清理。

比如用方括号取字典值时,键不存在会抛 KeyError;想安全地接住它,就 except KeyError。字典取值的完整规则可以查 Python 字典速查手册,里面列了字典常见的报错场景。finally 的价值在「哪怕 except 里又抛了新异常,清理代码依然跑得起来」,所以凡是打开文件、建连接、加锁,都该把释放动作放进 finally,别依赖程序「正常结束」才去做。

else 和 finally 经常一起用:else 处理成功后的业务,finally 处理无论如何都要做的收尾。记住一个口诀——可能错的进 try,接得住的进 except,成功才做的进 else,必做的进 finally。把这条理清,异常处理的骨架就稳了。

四、方法三:自定义异常 + with 上下文(最专业)

适合谁:写库、写业务模块,需要表达「业务语义」的失败;或想让资源清理自动化。代价:要定义自己的异常类,初看多一步,但长期维护更省心。

class BalanceError(Exception):
    """余额不足时抛出"""
    pass


def withdraw(balance, amount):
    if amount > balance:
        raise BalanceError(f"余额 {balance} 不足,无法取出 {amount}")
    return balance - amount


try:
    withdraw(100, 200)
except BalanceError as e:
    print("业务失败:", e)

自定义异常只需让自己的类继承 Exception(或它的子类),通常一行 pass 就够;需要带数据时,在 raise 时把信息传进去。它的好处是:调用方可以精确 except BalanceError,而不会误伤别的异常,业务意图一眼可读。注意要继承 Exception 而不是 BaseException,后者连 KeyboardInterrupt 这类系统信号都涵盖,不该被你自定义异常污染。

另一个专业写法是 with 语句(上下文管理器)。像文件、数据库连接这类「用完必须关」的资源,用 with 包起来,Python 会在退出时自动调用清理逻辑,等价于帮你写好了 finally。比起手写 try-finally,with 更短、更不容易漏关。

⚠️ 注意:raise 你的自定义异常时,尽量带上有意义的错误信息,别只写 raise BalanceError()。没有上下文的异常,排查时等于没说。

什么时候该自己抛异常?当内置异常无法准确表达你的业务失败时。比如「余额不足」「订单已关闭」这类领域概念,用 ValueError 也能凑,但语义模糊;定义一个明确的异常,调用方和未来的你都会感谢现在多写的这一行。

五、三种写法怎么选 + 高频坑

选型其实很简单:临时给一小段代码兜底,用方法一的 try-except 基础结构;需要区分成功/失败/清理三种分支,用方法二的 else/finally;写模块、要表达业务语义或自动化管理资源,用方法三的自定义异常 + with。日常八成的场景,方法二已经够用。

下面几个坑是新手最高频的:

  • 裸 except 吞异常:写 except:except Exception: 会把 KeyboardInterrupt、SystemExit 也接住,导致你连 Ctrl+C 都停不掉程序,还会掩盖真实 bug。能接具体就接具体。
  • 接错类型:try 里实际抛 KeyError,你却 except ValueError,网兜不住,异常照冒。动手前先确认这里到底会抛什么。
  • 在 except 里吞掉不处理:只写 except ValueError: pass 等于把错误静默吃掉,事后完全不知道发生过什么。至少记一条日志。
  • 资源没放进 finally/with:打开的文件、连接忘记关,久了泄漏。用 with 最省心。
  • 异常粒度太粗:一个 try 包半屏代码,出事了定位不到行。try 越窄,越好定位。

把这五条记牢,捕获异常基本不会再翻车。核心心法就一句:接得具体、清得干净、该抛就抛。

Python 捕获异常怎么选

总结

Python 捕获异常,日常先用 try-except 把可能出错的代码护住;需要区分正常/清理分支时,加上 else 和 finally;写模块要表达业务语义或自动管资源,用自定义异常配合 with。要点带走:

  • except 要接具体异常类型,别用裸 except 把程序信号也吞掉;
  • else 放成功后的逻辑,finally 放必做的清理,两者职责分开;
  • 资源类操作用 with,让 Python 自动帮你收尾,少写 finally 也少漏关;
  • 业务失败该自己 raise 自定义异常,别拿内置异常凑,语义才清楚。

延伸学习

想把异常处理这块系统补齐,可以按这个顺序来:

  1. 先过一遍 Python3 入门课程
  2. 想看另一种讲法,参考 Python 异常捕获笔记 里更细的实战例子;
  3. 想系统学,Python零基础到高薪就业

常见问题

Q:裸 except 和 except Exception 有什么区别?

A:两者都会接住绝大多数异常,区别在于 except:(不带任何类型)连 KeyboardInterrupt、SystemExit 这类系统级信号也一并接住,导致你按 Ctrl+C 都停不掉程序;except Exception: 至少放过了这两类。但日常仍建议接具体异常,比如 ValueError、KeyError,避免把本该暴露的 bug 悄悄盖住。

Q:else 和 finally 到底什么时候用?

A:else 在 try 块完全没出错时才执行,适合放「依赖 try 成功结果」的后续业务;finally 无论成功失败、哪怕 except 里又抛了新异常都会执行,适合放资源释放、关闭连接这类必做清理。口诀是:成功才做的进 else,必做的进 finally。多数需要清理资源的场景,用 with 比手写 finally 更省心。

Q:什么时候该自己定义异常,而不是直接用内置的?

A:当内置异常无法准确表达你的业务失败时。比如「余额不足」「订单已关闭」这类领域概念,用 ValueError 也能凑但语义模糊,调用方还得猜。定义一个继承 Exception 的自定义异常,调用方就能精确 except 它,业务意图一眼可读,长期维护成本低很多。

0 人点赞