2.13. 异常

未匹配的标注

异常

意外的 情况在你的程序中发生时就会产生异常。例如,当你尝试读取一个文件但它并不存在时,会发生什么?或者,当程序还在运行的时候,你删除了它会怎么样?这类情况会通过引发异常来处理。

相似地,如果你的程序有一些无效的语句会发生什么? 这由 Python 进行处理,它会举手并告诉你这里有一个错误。

错误

考虑一个简单的 print 函数调用。当 print 被错误拼写成 Print 会发生什么?注意字母大写。这种情况下, Python 将会 引发( raise ) 一个语法错误。

>>> Print("Hello World")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'Print' is not defined
>>> print("Hello World")
Hello World

对这个错误的处理是,观察到 NameError 错误被引发并打印出这个错误发生的位置。

异常

我们将尝试读取用户的输入。我们输入下面的第一行代码并按下 Enter 执行。当你的计算机提示你输入时,在 Mac 上按下 [ctrl-d] 或者在 Windows 上按下 [ctrl-z] 来观察会发生什么(如果你使用的是 Windows 系统而以上两个选择都无效时,你可以尝试在命令行窗口使用 [ctrl-c] 来产生 KeyboardInterrupt 错误)。

>>> s = input('Enter something --> ')
Enter something --> 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
EOFError

Python 引发了一个名为 EOFError 的错误,它的意思是发现了一个不该出现的 文件末尾( end of file) 符号(可以用 ctrl-d 表示)。

处理异常

我们可以用 try..except 语句来处理异常。我们简单地把正常语句放入 try 语句块,并把所有错误处理程序放入 except 语句块。

示例(保存为 exceptions_handle.py ):

try:
    text = input('Enter something --> ')
except EOFError:
    print('Why did you do an EOF on me?')
except KeyboardInterrupt:
    print('You cancelled the operation.')
else:
    print('You entered {}'.format(text))

输出:

# 按下 ctrl + d
$ python exceptions_handle.py
Enter something --> Why did you do an EOF on me?

# 按下 ctrl + c
$ python exceptions_handle.py
Enter something --> ^CYou cancelled the operation.

$ python exceptions_handle.py
Enter something --> No exceptions
You entered No exceptions

工作原理

我们将所有可能引发异常或错误的语句放入 try 语句块中,然后将对应错误或异常的处理程序放入 except 子句(程序块)中。 except 子句会处理单个特定的错误或异常,或是一个带括号的错误或异常列表。 如果没有提供错误或异常的名字, 它将处理 所有的 错误和异常。

请注意,每个 try 子句之后,至少要有一个与之关联的 except 子句。否则, 一个单独的 try 语句块有什么意义?

如果有任何未处理的错误和异常,默认的 Python 处理程序将被调用,它只会终止程序运行并打印出一条异常信息。我们在之前的操作中已经见过了。

你可以使用一个与 try..except 语句块关联的 else 子句。 else 子句在没有错误发生时将会执行。

在下一个例子中,我们将会看到如何获取一个异常对象,这样我们就可以获得额外的异常信息。

引发异常

你可以用 raise 语句 引发( raise ) 异常,需要提供错误或异常的名字以及被 抛出( thrown ) 的异常对象。

你用于引发异常的错误和异常应该是一个直接或间接地派生自 Exception 类的类。

示例(保存为 exceptions_raise.py ):

class ShortInputException(Exception):
    '''用户定义的异常对象'''
    def __init__(self, length, atleast):
        Exception.__init__(self)
        self.length = length
        self.atleast = atleast

try:
    text = input('Enter something --> ')
    if len(text) < 3:
        raise ShortInputException(len(text), 3)
    # 其他程序可以在这里正常执行
except EOFError:
    print('Why did you do an EOF on me?')
except ShortInputException as ex:
    print(('ShortInputException: The input was ' +
           '{0} long, expected at least {1}')
          .format(ex.length, ex.atleast))
else:
    print('No exception was raised.')

输出:

$ python exceptions_raise.py
Enter something --> a
ShortInputException: The input was 1 long, expected at least 3

$ python exceptions_raise.py
Enter something --> abc
No exception was raised.

工作原理

这里,我们创建了一个我们自己的异常类型这个新的异常类型被命名为 ShortInputException 。它有两个字段, length 是给出输入的长度, atleast 是程序所期望的最小长度。

except 子句中,我们注意到错误的类通过 as 把错误或异常对应的对象储存到了命名的变量中。这类似于函数调用中的变量和参数。在特定的 except 子句中,我们用异常对象的 lengthatleast 字段向用户输出适当的信息。

Try … Finally

假设你要在你的程序中读取一个文件。如何保证无论是否引发错误,文件对象都被正确关闭? 可以使用 finally 语句块来完成。

把这段程序保存为 exceptions_finally.py

import sys
import time

f = None
try:
    f = open("poem.txt")
    # 我们通常读取文件的语句
    while True:
        line = f.readline()
        if len(line) == 0:
            break
        print(line, end='')
        sys.stdout.flush()
        print("Press ctrl+c now")
        # 让程序保持运行一段时间
        time.sleep(2)
except IOError:
    print("Could not find file poem.txt")
except KeyboardInterrupt:
    print("!! You cancelled the reading from the file.")
finally:
    if f:
        f.close()
    print("(Cleaning up: Closed the file)")

输出:

$ python exceptions_finally.py
Programming is fun
Press ctrl+c now
^C!! You cancelled the reading from the file.
(Cleaning up: Closed the file)

工作原理

我们做了正常的文件读取,但我们在每行输出之后用 time.sleep 函数特意加入了2秒的休眠,这样程序就会缓慢运行(通常 Python 运行很快)。当程序还在运行时,按下 ctrl + c 来终止或取消程序运行。.

观察到 KeyboardInterrupt 异常被抛出以及程序退出。但是,在程序退出前, finally 子句被执行,文件对象总是被正确关闭。

请注意, Python 将变量中的 0 、 None 、空数组和空集合都视为 False 。这就是为什么我们可以在上面的代码中使用 if f:

还要注意,我们在 print 之后使用 sys.stdout.flush() ,这样就可以立刻输出到屏幕上。

with 语句

try 语句块中获取资源,并最终在 finally 语句块中释放资源是一种常见做法。因此使用 with 语句可以以更清晰的代码风格实现以上过程:

保存为 exceptions_using_with.py

with open("poem.txt") as f:
    for line in f:
        print(line, end='')

工作原理

输出应该与之前一个示例相同。这里的不同点是,我们将 with 语句和 open 函数一起使用——我们让 with open 自动完成文件关闭。

with 语句隐藏地使用了一个规则。它获取了 open 语句返回的对象,这里我们称之为 “thefile” 。

它开始它下面的这个代码块前 总是 调用 thefile.__enter__ 函数,在离开这个代码块后 总是 调用 thefile.__exit__

因此,被我们写入 finally 语句块的代码会被 __exit__ 方法自动完成。这避免我们重复地显示使用 try..finally 语句。

关于这个话题的更多讨论不属于本书范围,请参考 PEP 343 获得更加详细的解释。

小结

我们已经讨论了 try..except 语句和 try..finally 语句的用法。我们看到了怎样创建一个我们自己的异常类型以及怎样引发一个异常。

接下来,我们将探索 Python 标准库。

本文章首发在 LearnKu.com 网站上。

本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
上一篇 下一篇
Summer
贡献者:2
讨论数量: 0
发起讨论 只看当前版本


暂无话题~