Python文档学习笔记(11.1)--错误和异常
错误和异常
Python(至少)有两种错误很容易区分:语法错误 和异常。
语法错误
略
异常
>>> 10 * (1/0) Traceback (most recent call last): File "<stdin>", line 1, in ? ZeroDivisionError: division by zero >>> 4 + spam*3 Traceback (most recent call last): File "<stdin>", line 1, in ? NameError: name 'spam' is not defined >>> '2' + 2 Traceback (most recent call last): File "<stdin>", line 1, in ? TypeError: Can't convert 'int' object to str implicitly
最后一行的错误消息指示发生了什么事。异常有不同的类型,其类型会作为消息的一部分打印出来:在这个例子中的类型有ZeroDivisionError
、NameError
和 TypeError
.打印出来的异常类型的字符串就是内置的异常的名称。这对于所有内置的异常是正确的,但是对于用户自定义的异常就不一定了(尽管这是非常有用的惯例)。标准异常的名称都是内置的标识符(不是保留的关键字)。
这一行最后一部分给出了异常的详细信息和引起异常的原因。
错误信息的前面部分以堆栈回溯的形式显示了异常发生的上下文。通常调用栈里会包含源代码的行信息,但是来自标准输入的源码不会显示行信息。
内置的异常 列出了内置的异常以及它们的含义。
处理异常
可以通过编程来选择处理部分异常。
>>> while True: ... try: ... x = int(input("Please enter a number: ")) ... break ... except ValueError: ... print("Oops! That was no valid number. Try again...") ...
try
语句按以下方式工作。
- 首先,执行 try 子句 (
try
和except
关键字之间的语句)。 - 如果未发生任何异常,忽略 except 子句 且
try
语句执行完毕。 - 如果在 try 子句执行过程中发生异常,跳过该子句的其余部分。如果异常的类型与
except
关键字后面的异常名匹配, 则执行 except 子句,然后继续执行try
语句之后的代码。 - 如果异常的类型与 except 关键字后面的异常名不匹配,它将被传递给上层的
try
语句;如果没有找到处理这个异常的代码,它就成为一个 未处理异常 ,程序会终止运行并显示一条如上所示的信息。
try
语句可能有多个子句,以指定不同的异常处理程序。不过至多只有一个处理程序将被执行。处理程序只处理发生在相应 try 子句中的异常,不会处理同一个 try
子句的其他处理程序中发生的异常。一个 except 子句可以用带括号的元组列出多个异常的名字,例如:
... except (RuntimeError, TypeError, NameError): ... pass
最后一个 except 子句可以省略异常名称,以当作通配符使用。使用这个非常小心,以这种方式很容易掩盖真正的编程错误!它还可以用来打印一条错误消息,然后重新引发异常 (让调用者也去处理这个异常):
import sys try: f = open('myfile.txt') s = f.readline() i = int(s.strip()) except OSError as err: print("OS error: {0}".format(err)) except ValueError: print("Could not convert data to an integer.") except: print("Unexpected error:", sys.exc_info()[0]) raise
try
...except
语句有一个可选的 else 子句 ,其出现时,必须放在所有 except 子句的后面。如果需要在 try 语句没有抛出异常时执行一些代码,可以使用这个子句。例如:
for arg in sys.argv[1:]: try: f = open(arg, 'r') except IOError: print('cannot open', arg) else: print(arg, 'has', len(f.readlines()), 'lines') f.close()
使用 else
子句比把额外的代码放在 try
子句中要好,因为它可以避免意外捕获不是由 try
... except
语句保护的代码所引发的异常。
except 子句可以在异常名之后指定一个变量。这个变量将绑定于一个异常实例,同时异常的参数将存放在 instance.args
中。为方便起见,异常实例定义了 __str__()
,因此异常的参数可以直接打印而不必引用 .args
。也可以在引发异常之前先实例化一个异常,然后向它添加任何想要的属性。
>>> try: ... raise Exception('spam', 'eggs') ... except Exception as inst: ... print(type(inst)) # the exception instance ... print(inst.args) # arguments stored in .args ... print(inst) # __str__ allows args to be printed directly, ... # but may be overridden in exception subclasses ... x, y = inst.args # unpack args ... print('x =', x) ... print('y =', y) ... <class 'Exception'> ('spam', 'eggs') ('spam', 'eggs') x = spam y = eggs
对于未处理的异常,如果它含有参数,那么参数会作为异常信息的最后一部分打印出来。
异常处理程序不仅能处理在try子句直接发生的异常,也能处理try子句调用的函数中(即使是间接地)发生的异常。例如:
>>> def this_fails(): ... x = 1/0 ... >>> try: ... this_fails() ... except ZeroDivisionError as err: ... print('Handling run-time error:', err) ... Handling run-time error: int division or modulo by zero
抛出异常
raise
语句允许程序员强行引发一个指定的异常。例如:
>>> raise NameError('HiThere') Traceback (most recent call last): File "<stdin>", line 1, in ? NameError: HiThere
raise
的唯一参数指示要引发的异常。它必须是一个异常实例或异常类(从 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 ? NameError: HiThere