跳到主要内容

错误处理

1. 为何用 try...except 而非错误码

  • 错误码表示出错(如 open 失败返回 -1)会导致:正常返回值与错误码混在一起,调用方要写大量 if 判断;出错还要一级一级上报直到有人处理。
  • 高级语言通常内置 try...except...finally 机制,Python 同理:出错时跳转到 except,不必每层都判断错误码。

2. try / except / else / finally

  • try:运行可能出错的代码。
  • except:捕获异常;出错时不再执行 try 后续代码,直接进入对应 except;可写多个 except 捕获不同类型(如 ValueErrorZeroDivisionError)。
  • else:放在 except 之后,没有发生异常时执行。
  • finally无论是否异常都会执行(可省略);常用于收尾(如关闭资源)。
try:
print('try...')
r = 10 / int('a')
print('result:', r)
except ValueError as e:
print('ValueError:', e)
except ZeroDivisionError as e:
print('ZeroDivisionError:', e)
else:
print('no error!')
finally:
print('finally...')
  • except 异常类型 as e:将异常实例赋给 e,便于打印或记录。

3. 异常是 class、捕获顺序

  • Python 的错误/异常都是 class,均继承自 BaseException(常见继承关系见 官方文档)。
  • except 会捕获该类型及其子类;因此子类 except 必须写在父类 except 前面,否则子类永远捕获不到(如 UnicodeErrorValueError 的子类,先写 except ValueError 就会把 UnicodeError 也捕获掉)。

4. 跨多层调用

  • 不需要在每一层都写 try/except;只要在合适的层次(如顶层 main)捕获即可。错误会向上抛,只要某一层捕获就能处理,从而减少重复的 try/except。
def foo(s):
return 10 / int(s)
def bar(s):
return foo(s) * 2
def main():
try:
bar('0')
except Exception as e:
print('Error:', e)
finally:
print('finally...')

5. 调用栈(Traceback)

  • 若错误未被捕获,会一直向上抛,最终由解释器捕获并打印 Traceback,然后程序退出。
  • 解读顺序:从上往下是调用链(main → bar → foo → 出错行),最下面错误类型和原因,也是错误源头。定位错误时要分析调用栈,找到真正出错的那一行。

6. 记录错误:logging

  • 捕获后若希望既记录错误又让程序继续运行,可用 logging 模块:logging.exception(e) 会打印完整错误堆栈;还可通过配置将错误写入日志文件,便于事后排查。
import logging
try:
bar('0')
except Exception as e:
logging.exception(e) # 打印堆栈,程序继续
print('END')

7. 抛出错误:raise

  • 错误是有意创建并抛出的;内置函数会抛各种类型,自定义函数也可用 raise 抛出
  • 自定义异常类:继承自合适的内置异常(如 ValueError),需要时再定义;能沿用内置类型时尽量用内置(ValueError、TypeError 等)。
class FooError(ValueError):
pass

def foo(s):
n = int(s)
if n == 0:
raise FooError('invalid value: %s' % s)
return 10 / n
  • raise 不带参数:在 except 里写 raise 表示把当前异常原样继续向上抛;常用于“本层只记录或转换,由上层处理”。
  • 转换异常类型:在 except 里 raise 另一种Error(...),可把一种异常转为另一种;只应在逻辑合理时使用,不要把毫不相干的类型乱转(如 IOError 转成 ValueError)。
def bar():
try:
foo('0')
except ValueError as e:
print('ValueError!')
raise # 原样继续向上抛,让上层处理

小结

要点说明
try/except/finallytry 运行可能出错代码;except 捕获并处理;finally 总执行
else紧接 except,无异常时执行
异常是 class继承 BaseException;except 捕获该类型及子类,子类 except 写前面
跨层捕获不必每层都 try,在合适层次捕获即可
Traceback从上往下看调用链,最下面是错误类型和源头
logging.exception(e)记录错误堆栈且程序可继续;可配置写日志文件
raise抛出异常;无参 raise 原样上抛;可定义自定义异常类(继承 ValueError 等)

记忆:try 包可能出错、except 按类型抓、finally 必走;能跨层抓、看栈从下找源头;要记日志用 logging;要抛用 raise。