Python 异常处理

fansichao 2021-10-23 16:16:35
Categories: Tags:

简介说明

异常

基础使用

try..except..else..finally

1
2
3
4
5
6
7
8
9
10
import traceback
try:
x = a + 1
except (NameError, ZeroDivisionError) as err:
print('>>> Error')
print(traceback.format_exc())
else:
x += 1
finally:
print('> 执行完毕')

Raise 语句

1
2
3
4
5
# raise 语句允许程序员强制发生指定的异常
raise NameError('HiThere')

# raise 如果你需要确定是否引发了异常但不打算处理它
raise

用户自定义异常

1
2
3
4
5
6
7
8
9
10
11
12
13
# 异常通常应该直接或间接地从 Exception 类派生
# 大多数异常都定义为名称以“Error”结尾,类似于标准异常的命名
class InputError(Error):
"""Exception raised for errors in the input.

Attributes:
expression -- input expression in which the error occurred
message -- explanation of the error
"""

def __init__(self, expression, message):
self.expression = expression
self.message = message

定义清理操作 finally 语句

try 的可选子句 finally 用于定义必须在所有情况下执行的清理操作

1
2
3
4
5
try:
raise KeyboardInterrupt
finally:
print('Goodbye, world!')
# finally 语句无论是否发生异常都会执行

更复杂的异常情况(简单来说 finally 语句无论如何都会执行)

预定义的清理操作

Python 本身提供了很多语法范式简化了异常处理,例如:

以上这些都是 Python 自身封装好的语法范式,在处理这些事件的时候应避免使用 try/except/finally 的思维来处理。

小结

异常层级分析

异常层级

1
2
3
4
5
try:
pass
# 捕获 Exception 内部的异常
except Exception as err:
pass

详细异常层级分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
BaseException
+-- SystemExit
+-- KeyboardInterrupt
+-- GeneratorExit
+-- Exception
+-- StopIteration
+-- StandardError
| +-- BufferError
| +-- ArithmeticError
| | +-- FloatingPointError
| | +-- OverflowError
| | +-- ZeroDivisionError
| +-- AssertionError
| +-- AttributeError
| +-- EnvironmentError
| | +-- IOError
| | +-- OSError
| | +-- WindowsError (Windows)
| | +-- VMSError (VMS)
| +-- EOFError
| +-- ImportError
| +-- LookupError
| | +-- IndexError
| | +-- KeyError
| +-- MemoryError
| +-- NameError
| | +-- UnboundLocalError
| +-- ReferenceError
| +-- RuntimeError
| | +-- NotImplementedError
| +-- SyntaxError
| | +-- IndentationError
| | +-- TabError
| +-- SystemError
| +-- TypeError
| +-- ValueError
| +-- UnicodeError
| +-- UnicodeDecodeError
| +-- UnicodeEncodeError
| +-- UnicodeTranslateError
+-- Warning
+-- DeprecationWarning
+-- PendingDeprecationWarning
+-- RuntimeWarning
+-- SyntaxWarning
+-- UserWarning
+-- FutureWarning
+-- ImportWarning
+-- UnicodeWarning
+-- BytesWarning

异常使用技巧

传递异常

有时我们会在捕捉到一个异常后重新引发它(传递异常),实现起来很简单,使用不带参数的 raise 语句即可,例如

1
2
3
4
5
6
7
8
9
10
11
def f1():
print(1/0)

def f2():
try:
f1()
except Exception as e:
print('something worng')
raise

f2()

只做精确的异常捕获

在 Python 中使用异常捕获时应捕获尽可能精确的异常类型,而不是模糊的 Exception。

别让异常破坏代码抽象分层的一致性

很多场景下我们会对异常类进行包装,方便在产生已知异常时自定义错误信息,这样做能大大提高后续的编码效率,但在使用时如果没有做好分层处理很容易击穿代码的抽象分层逻辑,具体案例请参考 Python 工匠: 异常处理的三个好习惯
为了避免因为使用错误的异常处理方式导致代码的抽象分层逻辑被打破:

  1. 让模块只调用与当前抽象层级一致的异常类,既不能高于当前抽象层级,也不能低于当前抽象层级
  2. 在需要跨层级调用异常类时应通过异常包装与转换的方法进行,而不是直接跨层级调用异常类

异常处理不应该喧宾夺主

异常处理逻辑太多,以至于扰乱了代码核心逻辑

最佳实践

最佳实践不限于编程语言,只是一些规则和填坑后的收获。

  1. 只处理你知道的异常,避免捕获所有异常然后吞掉它们。
  2. 抛出的异常应该说明原因,有时候你知道异常类型也猜不出所以然的。
  3. 避免在 catch 语句块中干一些没意义的事情。
  4. 不要使用异常来控制流程,那样你的程序会无比难懂和难维护。
  5. 如果有需要,切记使用 finally 来释放资源。
  6. 如果有需要,请不要忘记在处理异常后做清理工作或者回滚操作。

异常速查表

Python_异常速查表

参考资源