Python中的异常处理

在程序中使用异常处理来提高程序的健壮性,隐藏程序的内部实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
while True:
n1 = input("> ")
n2 = input("> ")

try:
n1 = int(n1)
n2 = int(n2)
result = n1 + n2
except Exception as ex: # ex是Exception类的一个对象
print(ex) # 执行了ex对象的__str__方法
# ex中封装了所有的错误信息

------------
> 1
> str
invalid literal for int() with base 10: 'str'
>

异常的捕获属于程序正常运行的功能,和报错退出有本质的区别,上面的程序中,虽然有错误,但是程序并没有被终止

常见的异常种类

1
2
3
4
5
6
7
8
9
10
11
12
13
AttributeError 试图访问一个对象没有的树形,比如foo.x,但是foo没有属性x
IOError 输入/输出异常;基本上是无法打开文件
ImportError 无法引入模块或包;基本上是路径问题或名称错误
IndentationError 语法错误(的子类) ;代码没有正确对齐
IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5]
KeyError 试图访问字典里不存在的键
KeyboardInterrupt Ctrl+C被按下
NameError 使用一个还未被赋予对象的变量
SyntaxError Python代码非法,代码不能编译(个人认为这是语法错误,写错了)
TypeError 传入对象类型与要求的不符合
UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量,
导致你以为正在访问它
ValueError 传入一个调用者不期望的值,即使值的类型是正确的

更多的异常种类

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
ArithmeticError
AssertionError
AttributeError
BaseException
BufferError
BytesWarning
DeprecationWarning
EnvironmentError
EOFError
Exception
FloatingPointError
FutureWarning
GeneratorExit
ImportError
ImportWarning
IndentationError
IndexError
IOError
KeyboardInterrupt
KeyError
LookupError
MemoryError
NameError
NotImplementedError
OSError
OverflowError
PendingDeprecationWarning
ReferenceError
RuntimeError
RuntimeWarning
StandardError
StopIteration
SyntaxError
SyntaxWarning
SystemError
SystemExit
TabError
TypeError
UnboundLocalError
UnicodeDecodeError
UnicodeEncodeError
UnicodeError
UnicodeTranslateError
UnicodeWarning
UserWarning
ValueError
Warning
ZeroDivisionError

Exception

在捕获上面指定异常的时候,我们常常需要指定多个异常区捕获,但有时还是无法全部全部预料到会出现哪种类型的异常,这个时候,Python提供给我们一个万能的异常捕获Exception

那单独捕获异常存在意义是什么呢?在很多时候,我们需要捕获特定的异常来记录日志,或是对某些指定的异常做特殊的处理,这个时候指定捕获某类异常就非常有用啦,为了同时兼顾程序的健壮性,可以像if elif else一样来指定捕获的异常同时兼顾捕获所有的异常,类似于下面的写法

1
2
3
4
5
6
7
8
try:
# ...
except ValueError as ex:
print(ex)
except IndexError as ex:
print(ex)
except Exception as ex:
print(ex)

这样把指定的异常区分开来

注意:Exception一定要放在最后!

异常处理的完整代码块

1
2
3
4
5
6
7
8
9
10
11
12
try:
# 主代码块
pass
except KeyError,e:
# 异常时,执行该块
pass
else:
# 主代码块执行完,执行该块
pass
finally:
# 无论异常与否,最终执行该块
pass
  • 当 try 中的主代码块执行正确时, 将执行 else 里面的代码, 最后执行 finally 里面的代码
  • 当 try 中的主代码块执行出现异常时, 将执行 except 里面的代码, 最后执行 finally 里面的代码

主动触发异常

1
2
3
4
try:
raise Exception("主动抛出异常") # 创建了一个Exception对象,self.message = "主动抛出异常"
except Exception as ex:
print(ex) # __str__ return self.message
1
2
3
4
try:
raise ValueError("主动抛出值异常") # 创建了一个ValueError对象,self.message = "主动抛出异常"
except ValueError as ex:
print(ex) # __str__ return self.message

自定义异常

1
2
3
4
5
6
7
8
9
10
11
12
class PsException(Exception):

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

def __str__(self):
return self.message

try:
raise PsException('Ps的异常')
except PsException as ex:
print(ex)

断言

语法:assert 条件表达式

条件表达式为假则触发AssertionError异常

1
2
3
assert 1 == 1

assert 1 == 2

assert的应用场景:在执行某些操作之前,如果不符合我指定的某些规则,抛出异常

1
2
3
4
5
6
# multiprocessing源码截取
from multiprocessing import pool

p = pool.Pool()

p.join()
1
2
3
4
5
6
7
8
def join(self):
util.debug('joining pool')
assert self._state in (CLOSE, TERMINATE)
self._worker_handler.join()
self._task_handler.join()
self._result_handler.join()
for p in self._pool:
p.join()

*断言与 raise 的区别: 可以简单理解为 断言=if...else... + raise *