8.1.语法错误#
又称解析错误
报错信息包括:出错的行;显示指向检测到错误位置的小箭头;文件名
8.2.异常#
在执行时检测到的错误称为异常
报错信息包括:异常的类型;错误细节
8.3.异常的处理#
try语句的工作原理:
1.执行try子句
2.没有触发异常,跳过except子句
3.触发异常,跳过try子句剩下部分,并且将异常与except后关键字进行匹配“如果匹配,则执行except子句;如果为未匹配,则停止执行并输出一条错误信息
特性:
一个 try 可以有多个普通 except 子句,对于一次抛出的普通异常,只有第一个匹配的 except 子句会执行。一个 except 子句中的类匹配的异常将是该类本身的实例或其所派生的类的实例(但反过来则不可以 — 列出派生类的 except 子句 不会匹配其基类的实例)
人话:基类(父类)异常可以匹配子类异常,反之不可以
so,需要主要异常匹配的顺序
异常参数
except子句可能在异常后指定一个变量(通过as),被绑定到异常实例,该实例有一个args属性,内置异常类型定义了 __str__() 来打印所有参数而不必显式地访问 .args。
异常之间的关系
BaseException 是所有异常的共同基类。它的一个子类,Exception,是所有非致命异常的基类。不是 Exception 的子类的异常通常不被处理,因为它们被用来指示程序应该终止。它们包括由 sys.exit() 引发的 SystemExit,以及当用户希望中断程序时引发的 KeyboardInterrupt。
异常匹配的注意点:
Exception 可以被用作通配符,捕获(几乎)一切。然而,好的做法是,尽可能具体地说明我们打算处理的异常类型,并允许任何意外的异常传播下去。
try…except语句具有可选的else语句
异常处理程序还会处理在try子句中调用的函数的异常
8.4.触发异常#
使用raise语句,强制触发指定类型的异常
唯一的参数是要触发的异常,这个参数必须是异常实例或者异常类,如果是异常类,会隐式调用无参数的构造函数来创建一个异常实例
8.5.异常链#
raise a from b语句来显式表明一个异常(a)是另一个异常(b)的直接后果:此处的b必须为实例或者none,而未none时,表示禁用自动异常链
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| def func():
raise ConnectionError
try:
func()
except ConnectionError as exc:
raise RuntimeError('Failed to open database') from exc
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
func()
~~~~^^
File "<stdin>", line 2, in func
ConnectionError
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
raise RuntimeError('Failed to open database') from exc
RuntimeError: Failed to open database
|
8.6.用户自定义异常#
程序可以通过创建新的异常类命名自己的异常 (Python 类的内容详见 类)。 不论是以直接还是间接的方式,异常都应从 Exception 类派生。
异常类可以被定义成能做其他类所能做的任何事,但通常应当保持简单,它往往只提供一些属性,允许相应的异常处理程序提取有关错误的信息。
大多数异常命名都以"Error"结尾,类似标准异常的命名。
许多标准模块定义了自己的异常,以报告他们定义的函数中可能出现的错误。
8.7.定义清理操作#
try-finally语句:定义在所有情况下都要执行的操作,通常在异常处理中用于清理操作
一些复杂的触发异常场景,感觉没什么意思,自己看看吧
- 如果执行
try 子句期间触发了某个异常,则某个 except 子句应处理该异常。如果该异常没有 except 子句处理,在 finally 子句执行后会被重新触发。 except 或 else 子句执行期间也会触发异常。同样,该异常会在 finally 子句执行之后被重新触发。- 如果
finally 子句执行 break、 continue 或 return 语句,异常不重新引发。这可能会引起混淆,因此不鼓励使用。从 3.14 版开始,编译器会为它发出一个 SyntaxWarning (参见 PEP 765)。 - 如果执行
try 语句时遇到 break、continue 或 return 语句,则 finally 子句在执行 break、continue 或 return 语句之前执行。 - 如果一个
finally 子句包含一个 return 语句,返回的值将是来自 finally 子句的 return 语句,而不是来自 try 子句的 return 语句。 这可能会引起混淆,因此不提倡使用。 从 3.14 版开始,编译器会为它发出一个 SyntaxWarning (参见 PEP 765)。
通常用于释放外部资源的逻辑
8.8.预定义的清理操作(没啥意思,自己看)#
某些对象定义了不需要该对象时要执行的标准清理操作。无论使用该对象的操作是否成功,都会执行清理操作。比如,下例要打开一个文件,并输出文件内容:
1
2
| for line in open("myfile.txt"):
print(line, end="")
|
这个代码的问题在于,执行完代码后,文件在一段不确定的时间内处于打开状态。在简单脚本中这没有问题,但对于较大的应用程序来说可能会出问题。with 语句支持以及时、正确的清理的方式使用文件对象:
1
2
3
| with open("myfile.txt") as f:
for line in f:
print(line, end="")
|
语句执行完毕后,即使在处理行时遇到问题,都会关闭文件 f。和文件一样,支持预定义清理操作的对象会在文档中指出这一点。
8.9.引发和处理多个不相关的异常#
场景:继续执行收集多个错误而不是引发第一个异常
方法一:内置的 ExceptionGroup 打包了一个异常实例的列表,这样它们就可以一起被引发。它本身就是一个异常,所以它可以像其他异常一样被捕获。
方法二:使用except替代except,有选择地处理异常,注意此处地except子句就不再是只能执行一个了
例子:
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
| def f():
raise ExceptionGroup(
"group1",
[
OSError(1),
SystemError(2),
ExceptionGroup(
"group2",
[
OSError(3),
RecursionError(4)
]
)
]
)
try:
f()
except* OSError as e:
print("There were OSErrors")
except* SystemError as e:
print("There were SystemErrors")
There were OSErrors
There were SystemErrors
+ Exception Group Traceback (most recent call last):
| File "<stdin>", line 2, in <module>
| f()
| ~^^
| File "<stdin>", line 2, in f
| raise ExceptionGroup(
| ...<12 lines>...
| )
| ExceptionGroup: group1 (1 sub-exception)
+-+---------------- 1 ----------------
| ExceptionGroup: group2 (1 sub-exception)
+-+---------------- 1 ----------------
| RecursionError: 4
+------------------------------------
|
注意,嵌套在一个异常组中的异常必须是实例,而不是类型。这是因为在实践中,这些异常通常是那些已经被程序提出并捕获的异常
8.10.用注释细化异常情况#
add_note(note)用于在异常被捕获之后添加信息到异常地注释列表中
例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
| try:
raise TypeError('bad type')
except Exception as e:
e.add_note('Add some information')
e.add_note('Add some more information')
raise
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
raise TypeError('bad type')
TypeError: bad type
Add some information
Add some more information
|
应用于异常组中,例如:
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
| def f():
raise OSError('operation failed')
excs = []
for i in range(3):
try:
f()
except Exception as e:
e.add_note(f'Happened in Iteration {i+1}')
excs.append(e)
raise ExceptionGroup('We have some problems', excs)
+ Exception Group Traceback (most recent call last):
| File "<stdin>", line 1, in <module>
| raise ExceptionGroup('We have some problems', excs)
| ExceptionGroup: We have some problems (3 sub-exceptions)
+-+---------------- 1 ----------------
| Traceback (most recent call last):
| File "<stdin>", line 3, in <module>
| f()
| ~^^
| File "<stdin>", line 2, in f
| raise OSError('operation failed')
| OSError: operation failed
| Happened in Iteration 1
+---------------- 2 ----------------
| Traceback (most recent call last):
| File "<stdin>", line 3, in <module>
| f()
| ~^^
| File "<stdin>", line 2, in f
| raise OSError('operation failed')
| OSError: operation failed
| Happened in Iteration 2
+---------------- 3 ----------------
| Traceback (most recent call last):
| File "<stdin>", line 3, in <module>
| f()
| ~^^
| File "<stdin>", line 2, in f
| raise OSError('operation failed')
| OSError: operation failed
| Happened in Iteration 3
+------------------------------------
|