一、错误
解析器会重复存在错误的行并显示一个指向该行中检测到错误的词元的‘箭头’。 错误可能是因为指向的词元之前缺少某个词元。
>>> while True print('Hello world')
File "<stdin>", line 1
while True print('Hello world')
^
SyntaxError: invalid syntax
二、异常
错误信息的最后一行说明程序遇到了什么类型的错误。异常有不同的类型,而类型名称会作为错误信息的一部分中打印出来。错误信息开头用堆栈回溯形式展示发生异常的语境。一般会列出源代码行的堆栈回溯;但不会显示从标准输入读取的行。
>>> 10 * (1/0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
三、异常处理
try 语句的工作原理如下:
首先,执行 try 子句 (try 和 except 关键字之间的(多行)语句)。
如果没有触发异常,则跳过 except 子句,try 语句执行完毕。
如果在执行 try 子句时发生了异常,则跳过该子句中剩下的部分。
如果异常的类型与 except 关键字后指定的异常相匹配,则会执行 except 子句,然后跳到 try/except 代码块之后继续执行。
如果发生的异常与 except 子句 中指定的异常不匹配,则它会被传递到外层的 try 语句中;如果没有找到处理句柄,则它是一个 未处理异常 且执行将停止并输出一条错误消息。
>>> while True:
... try:
... x = int(input("Please enter a number: "))
... break
... except ValueError:
... print("Oops! That was no valid number. Try again...")
...
Please enter a number: abc
Oops! That was no valid number. Try again...
Please enter a number: 123
四、异常触发
raise 语句支持强制触发指定的异常。raise 唯一的参数就是要触发的异常。这个参数必须是异常实例或异常类(派生自 BaseException 类,例如 Exception 或其子类)。如果传递的是异常类,将通过调用没有参数的构造函数来隐式实例化。如果只想判断是否触发了异常,但并不打算处理该异常,则可以使用更简单的 raise 语句重新触发异常:
>>> try:
... raise NameError('HiThere')
... except NameError:
... print('An exception flew by!')
... raise
...
An exception flew by!
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
NameError: HiThere
五、异常链
如果一个未处理的异常发生在 except 部分内,它将会有被处理的异常附加到它上面,并包括在错误信息中:
>>> try:
... open("database.sqlite")
... except OSError:
... raise RuntimeError("unable to handle error")
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
FileNotFoundError: [Errno 2] No such file or directory: 'database.sqlite'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
RuntimeError: unable to handle error
为了表明一个异常是另一个异常的直接后果, raise 语句允许一个可选的 from 子句,还允许使用 from None 表达禁用自动异常链:
>>> 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>
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>
RuntimeError: Failed to open database
#禁用异常链
>>> try:
... open('database.sqlite')
... except OSError:
... raise RuntimeError from None
...
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
RuntimeError
六、自定义异常
程序可以通过创建新的异常类命名自己的异常(Python 类的内容详见 类)。不论是以直接还是间接的方式,异常都应从 Exception 类派生。
异常类可以被定义成能做其他类所能做的任何事,但通常应当保持简单,它往往只提供一些属性,允许相应的异常处理程序提取有关错误的信息。
大多数异常命名都以 “Error” 结尾,类似标准异常的命名。
许多标准模块定义了自己的异常,以报告他们定义的函数中可能出现的错误。
七、清理操作
- 如果执行 try 子句期间触发了某个异常,则某个 except 子句应处理该异常。如果该异常没有 except 子句处理,在 finally 子句执行后会被重新触发。
- except 或 else 子句执行期间也会触发异常。 同样,该异常会在 finally 子句执行之后被重新触发。
- 如果 finally 子句中包含 break、continue 或 return 等语句,异常将不会被重新引发。
- 如果执行 try 语句时遇到 break,、continue 或 return 语句,则 finally 子句在执行 break、continue 或 return 语句之前执行。
- 如果 finally 子句中包含 return 语句,则返回值来自 finally 子句的某个 return 语句的返回值,而不是来自 try 子句的 return 语句的返回值。
>>> def divide(x, y):
... try:
... result = x / y
... except ZeroDivisionError:
... print("division by zero!")
... else:
... print("result is", result)
... finally:
... print("executing finally clause")
...
>>> divide(2, 1)
result is 2.0
executing finally clause
>>> divide(2, 0)
division by zero!
executing finally clause
八、预定义处理
某些对象定义了不需要该对象时要执行的标准清理操作。无论使用该对象的操作是否成功,都会执行清理操作。with 语句支持以及时、正确的清理的方式使用文件对象:
>>> with open("workfile") as f:
... for line in f:
... print(line, end="")
...
This is a test
('the answer', 42)
语句执行完毕后,即使在处理行时遇到问题,都会关闭文件 f。和文件一样,支持预定义清理操作的对象会在文档中指出这一点。