错误处理
1. 为何用 try...except 而非错误码
- 用错误码表示出错(如 open 失败返回 -1)会导致:正常返回值与错误码混在一起,调用方要写大量 if 判断;出错还要一级一级上报直到有人处理。
- 高级语言通常内置 try...except...finally 机制,Python 同理:出错时跳转到 except,不必每层都判断错误码。
2. try / except / else / finally
- try:运行可能出错的代码。
- except:捕获异常;出错时不再执行 try 后续代码,直接进入对应 except;可写多个 except 捕获不同类型(如
ValueError、ZeroDivisionError)。 - 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 前面,否则子类永远捕获不到(如 UnicodeError 是 ValueError 的子类,先写
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/finally | try 运行可能出错代码;except 捕获并处理;finally 总执行 |
| else | 紧接 except,无异常时执行 |
| 异常是 class | 继承 BaseException;except 捕获该类型及子类,子类 except 写前面 |
| 跨层捕获 | 不必每层都 try,在合适层次捕获即可 |
| Traceback | 从上往下看调用链,最下面是错误类型和源头 |
| logging.exception(e) | 记录错误堆栈且程序可继续;可配置写日志文件 |
| raise | 抛出异常;无参 raise 原样上抛;可定义自定义异常类(继承 ValueError 等) |
记忆:try 包可能出错、except 按类型抓、finally 必走;能跨层抓、看栈从下找源头;要记日志用 logging;要抛用 raise。