Contents

Python教程-错误和异常

本系列为作者在官网学习python时做的笔记,详见python官网

python标准库官方文档,查阅标准库相关的内容

python语言参考,查看python语法,该文档是简洁的,但试图做到准确和完整。非必要的内建对象类型和内建函数、模块的语义描述在 Python 标准库 中。

标准库和语言参考涵盖了python的所有内容,他们是互补的,如果在语言参考中没有找到需要的内容那么就一定在便准库文档中。

(至少)有两种可区分的错误:语法错误 和 异常。

语法错误

语法错误又称解析错误,解析器会输出出现语法错误的那一行,并显示一个“箭头”,指向这行里面检测到的第一个错误。 错误是由箭头指示的位置 上面 的 token 引起的(或者至少是在这里被检测出的)

异常

即使语句或表达式在语法上是正确的,但在尝试执行时,它仍可能会引发错误。 在执行时检测到的错误被称为 异常,异常不一定会导致严重后果。 不被程序处理异常,会直接让终止程序并打印错误信息:

1
2
3
4
>>> 10 * (1/0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero

错误信息的前一部分以堆栈回溯的形式显示发生异常时的上下文。

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

处理异常

1
2
3
4
5
6
7
>>> 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 语句可能有多个 except 子句,以指定不同异常的处理程序。 最多会执行一个处理程序。 处理程序只处理相应的 try 子句中发生的异常,而不处理同一 try 语句内其他处理程序中的异常。 一个 except 子句可以将多个异常命名为带括号的元组,例如:

1
2
... except (RuntimeError, TypeError, NameError):
...     pass

如果发生的异常和 except 子句中的类是同一个类或者是它的基类,则异常和 except 子句中的类是兼容的(反过来则不成立 — 派生类的 except 子句与基类不兼容)

可以使用as关键字给异常一个别名

最后的 except 子句可以省略异常名,以用作通配符。不推荐,该方式很容易掩盖真正的编程错误!不过可用于打印错误消息,然后重新引发异常(同样允许调用者处理异常):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
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 子句不引发异常时必须执行的代码来说很有用。 例如:

1
2
3
4
5
6
7
8
for arg in sys.argv[1:]:
    try:
        f = open(arg, 'r')
    except OSError:
        print('cannot open', arg)
    else:
        print(arg, 'has', len(f.readlines()), 'lines')
        f.close()

发生异常时,它可能具有关联值,也称为异常 参数 。参数的存在和类型取决于异常类型。

except 子句可以在异常名称后面指定一个变量。这个变量和一个异常实例绑定,它的参数存储在 instance.args 中。为了方便起见,异常实例定义了 str() ,因此可以直接打印参数而无需引用 .args 。也可以在抛出之前首先实例化异常,并根据需要向其添加任何属性。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
>>> 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

如果异常有参数,则它们将作为未处理异常的消息的最后一部分(‘详细信息’)打印。

抛出异常

raise 语句允许程序员强制发生指定的异常:

1
2
3
4
>>> raise NameError('HiThere')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: HiThere

raise 唯一的参数就是要抛出的异常。这个参数必须是一个异常实例或者是一个异常类(派生自 Exception 的类)。

如果你需要确定是否引发了异常但不打算处理它,可以使用更简单的 raise 语句形式重新引发异常

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
>>> 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

用户自定义异常

程序可以通过创建新的异常类来命名它们自己的异常(有关Python 类的更多信息,请参阅 类)。异常通常应该直接或间接地从 Exception 类派生。

在__init__方法里面添加属性

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class InputError(Error):
    """Exception raised for errors in the input.

    Attributes:
        expression -- input expression in which the error occurred
        message -- explanation of the error
    """

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

定义清理操作

try 语句有另一个可选finally子句,用于定义必须在所有情况下执行的清理操作:

1
2
3
4
5
6
7
8
9
>>> try:
...     raise KeyboardInterrupt
... finally:
...     print('Goodbye, world!')
...
Goodbye, world!
KeyboardInterrupt
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>

如果存在 finally 子句,则 finally 子句将作为 try 语句结束前的最后一项任务被执行。 finally 子句不论 try 语句是否产生了异常都会被执行:

  • try 子句抛出异常。如果被 except 子句处理,则finally在except子句处理后再执行。否则该异常会在 finally 子句执行后重新引发。
  • except 或 else 子句抛出异常,该异常会在 finally 子句执行后重新引发。
  • finally 子句将在 try 语句执行 break, continue 或 return 语句之前被执行。
  • finally 子句可以return。

预定义的清理操作

with 语句允许像文件这样的对象能够以一种确保它们得到及时和正确的清理的方式使用:

1
2
3
with open("myfile.txt") as f:
    for line in f:
        print(line, end="")

执行完语句后,即使在处理行时遇到问题,文件 f 也始终会被关闭。和文件一样,提供预定义清理操作的对象将在其文档中指出这一点。

 |