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

最后一行的错误消息指示发生了什么事。异常有不同的类型,其类型会作为消息的一部分打印出来:在这个例子中的类型有ZeroDivisionErrorNameErrorTypeError.打印出来的异常类型的字符串就是内置的异常的名称。这对于所有内置的异常是正确的,但是对于用户自定义的异常就不一定了(尽管这是非常有用的惯例)。标准异常的名称都是内置的标识符(不是保留的关键字)。

这一行最后一部分给出了异常的详细信息和引起异常的原因。

错误信息的前面部分以堆栈回溯的形式显示了异常发生的上下文。通常调用栈里会包含源代码的行信息,但是来自标准输入的源码不会显示行信息。

内置的异常 列出了内置的异常以及它们的含义。

处理异常

可以通过编程来选择处理部分异常。

>>> while True:
...     try:
...         x = int(input("Please enter a number: "))
...         break
...     except ValueError:
...         print("Oops!  That was no valid number.  Try again...")
...

try 语句按以下方式工作。

  • 首先,执行 try 子句tryexcept 关键字之间的语句)。
  • 如果未发生任何异常,忽略 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

 

posted @ 2019-06-01 22:48  IMWU  阅读(292)  评论(0编辑  收藏  举报