2.25. 错误处理

Python 中大多数运行时问题都以异常的形式表现出来 —— 这是一种有名称、有结构的方式,用来报告出了某种问题。ValueErrorTypeErrorKeyErrorOSErrorMemoryError 都是例子;每一个都是一个类,而抛出一个异常会停止当前调用,并在周围的代码中寻找处理程序。

2.25.1. try / except

try 包裹一段代码,以捕获它抛出的任何异常:

try:
    n = int(input_text)
except ValueError:
    n = 0

如果 int(...) 转换失败,控制流会跳转到 except 块,而不会让错误进一步向外传播。如果 input_text 是一个有效的整数字符串,except 块就会被跳过。

单个 try 可以带多个 except 块,每个捕获不同类型的错误:

try:
    value = data[key]
except KeyError:
    value = None
except TypeError:
    value = -1

Python 按顺序匹配;第一个其异常类匹配的块来处理该问题。捕获 Exception(几乎所有异常的基类)会处理任何错误;请把这种做法留给程序的最外层,因为那里的替代方案就是崩溃。

警告

一个裸 except:(关键字后面没有类)也会捕获 KeyboardInterrupt —— 即当你按下 IDE 的停止按钮中断正在运行的脚本时,IDE 所发送的异常。一个被裸 except: pass 包裹的循环会吞掉这个中断并继续运行,使得除了断电重启之外没有任何办法停止脚本。

当你确实需要广泛捕获时,请优先使用 except Exception: 而非裸 except:KeyboardInterrupt 继承自 BaseException 而非 Exception,因此 except Exception: 会让停止按钮仍然有效。

2.25.1.1. 检查异常

要读取附加在异常上的消息,用 as 给它命名:

try:
    f = open("data.txt")
except OSError as e:
    print("could not open file:", e)

as 绑定的变量只在 except 块内部有效。

2.25.2. else 和 finally

try 块有两个可选的附加部分。

else 仅在 try 顺利完成而未抛出异常时才运行:

try:
    value = compute()
except ValueError:
    print("bad input")
else:
    print("got", value)

把“成功时该做什么”放进 else 可以让 try 块保持简短 —— 只有那行可能失败的代码才应该放在 try 里。

finally 会在最后无论如何都运行 —— 无论 try 是成功了、抛出后被处理了,还是抛出后即将向外传播:

try:
    do_work()
finally:
    cleanup()
流程图展示了通过 try / except / else / finally 的 四条可能路径:成功走向 else 然后 finally;被捕获的 异常走向 except 然后 finally;未被捕获的异常走向 finally 然后向外传播。

finally 总会运行。else 只在没有异常的那条路径上运行。

对于大多数获取/释放模式,请优先使用 上下文管理器 而非 try / finally 组合 —— 资源本身会管理自己的清理。

2.25.3. 常见的内置异常

下面是你会经常遇到的异常的简短清单:

  • ValueError —— 类型正确,值错误(bytes([300]) —— 300 是正确的类型,但超出了 0..255 的有效字节范围)。

  • TypeError —— 类型完全不对(len(42))。

  • KeyError —— dict 中缺少某个键。

  • IndexError —— 索引越过了序列的末尾。

  • AttributeError —— 访问一个不存在的属性("abc".foo)。

  • OSError —— 文件系统或 I/O 故障。

  • MemoryError —— 堆内存耗尽。在内存受限的运行时上,这可能在正常运行期间发生 —— 而不仅仅是在极端情况下。