《Python基础教程(第3版)》笔记:异常

重点

第8章 异常

  1. 异常对象未被处理(或捕获)时,程序将终止并显示一条错误信息:traceback

    每个异常都是某个类的实例,

    • 如何创建异常

    • 如何自主引发异常:raise

      raise Exception

      raise Exception('hyperdrive overload')

      一些内置的异常类

      类名 描述
      Exception 几乎所有的异常类都是从它派生而来的
      AttributeError 引用属性或给它赋值失败时引发
      OSError 操作系统不能执行指定的任务(如打开文件)时引发,有多个子类
      IndexError 使用序列中不存在的索引时引发,为LookupError的子类
      KeyError 使用映射中不存在的键时引发,为LookupError的子类
      NameError 找不到名称(变量)时引发
      SyntaxError 代码不正确时引发
      TypeError 将内置操作或函数用于类型不正确的对象时引发
      ValueError 将内置操作或函数用于这样的对象时引发:其类型正确但包含的值不合适
      ZeroDivisionError 在除法或求模运算的第二个参数为零时引发
      • 自定义异常类:务必直接或间接地继承Exception

        class SomeException(Exception): pass

    • 如何处理异常

    • 捕获异常

      • try/except, 异常从函数向外传播到调用函数的地方,如果在这里也没有被捕获,异常将继续向程序的最顶层传播。

      • 导致进入except子句的异常将被作为异常上下文存储起来

      • 多个except子句

        使用一个except子句捕获多种异常,可在一个元组中指定这些异常

        try:x = int(input('value1:'))y = int(input('value2:'))print(x/y)
        except (ZeroDivisionError, TypeError, NameError):print('your numbers were bogus...')
        

        下面的代码,捕获所有异常,当不存在异常时,终止循环

        while True:try:x = int(input('value1:'))y = int(input('value2:'))print(x/y)except Exception as e:print('your numbers were bogus...', e)else:break
        
        • finally子句,可用于在发生异常时执行清理工作
    • 例子

      def describe_person(person):print('Description of ', person['name'])print('Age: ', person['age'])try:print('Occupation:', person['occupation'])except KeyError: pass
      
      try:obj.write
      except AttributeError:print('The object is not writeable')
      else:print('the object is writeable')
      

编写计算机程序时,通常能够区分正常和异常(不正常)情况。异常事件可能是错误(如试图除以零),也可能是通常不会发生的事情。为处理这些异常事件,可在每个可能发生这些事件的地方都使用条件语句。例如,对于每个除法运算,都检查除数是否为零。然而,这样做不仅效率低下、缺乏灵活性,还可能导致程序难以卒读。你可能很想忽略这些异常事件,希望它们不会发生,但Python提供功能强大的替代解决方案——异常处理机制

8.1 异常是什么

Python使用异常对象来表示异常状态,并在遇到错误时引发异常。异常对象未被处理(或捕获)时,程序将终止并显示一条错误消息(traceback)。

>>> 1 / 0
Traceback (most recent call last): File "<stdin>", line 1, in ?
ZeroDivisionError: integer division or modulo by zero

每个异常都是某个类(这里是ZeroDivisionError)的实例。你能以各种方式引发和捕获这些实例,从而逮住错误并采取措施,而不是放任整个程序失败

8.2 让程序沿着你指定的轨道出错

正如你看到的,出现问题时,将自动引发异常。先来看看如何自主地引发异常,还有如何创建异常,然后再学习如何处理这些异常。

8.2.1 raise 语句

要引发异常,可使用raise语句,并将一个类(必须是Exception的子类)或实例作为参数。将类作为参数时,将自动创建一个实例。下面的示例使用的是内置异常类Exception:

>>> raise Exception
Traceback (most recent call last):File "<pyshell#15>", line 1, in <module>raise Exception
Exception
>>> raise Exception('hyperdrive overload')
Traceback (most recent call last):File "<pyshell#16>", line 1, in <module>raise Exception('hyperdrive overload')
Exception: hyperdrive overload

表8-1 一些内置的异常类

类 名 描 述
Exception 几乎所有的异常类都是从它派生而来的
AttributeError 引用属性或给它赋值失败时引发
OSError 操作系统不能执行指定的任务(如打开文件)时引发,有多个子类
IndexError 使用序列中不存在的索引时引发,为LookupError的子类
KeyError 使用映射中不存在的键时引发,为LookupError的子类
NameError 找不到名称(变量)时引发
SyntaxError 代码不正确时引发
TypeError 将内置操作或函数用于类型不正确的对象时引发
ValueError 将内置操作或函数用于这样的对象时引发:其类型正确但包含的值不合适
ZeroDivisionError 在除法或求模运算的第二个参数为零时引发

8.2.2 自定义的异常类

那么如何创建异常类呢?就像创建其他类一样,但务必直接或间接地继承Exception(这意味着从任何内置异常类派生都可以)。因此,自定义异常类的代码类似于下面这样:
class SomeCustomException(Exception): pass

8.3 捕获异常

异常比较有趣的地方是可对其进行处理,通常称之为捕获异常。为此,可使用try/except语句。

try: x = int(input('Enter the first number: ')) y = int(input('Enter the second number: ')) print(x / y)
except ZeroDivisionError: print("The second number can't be zero!")

注意 异常从函数向外传播到调用函数的地方。如果在这里也没有被捕获,异常将向程序的最顶层传播。这意味着你可使用try/except来捕获他人所编写函数引发的异常。有关这方面的详细信息,请参阅8.4节。


8.3.1 不用提供参数

捕获异常后,如果要重新引发它(即:继续向上传播),可调用raise且不提供任何参数(也可显式地提供捕获到的异常,参见8.3.4节)

为说明这很有用,来看一个能够“抑制”异常ZeroDivisionError的计算器类。如果启用了这种功能,计算器将打印一条错误消息,而不让异常继续传播。在与用户交互的会话中使用这个计算器时,抑制异常很有用;但在程序内部使用时,引发异常是更佳的选择(此时应关闭“抑制”功能)。下面是这样一个类的代码:

class MuffledCalculator: muffled = False def calc(self, expr): try: return eval(expr) except ZeroDivisionError: if self.muffled: print('Division by zero is illegal') else: raise

注意 发生除零行为时,如果启用了“抑制”功能,方法calc将(隐式地)返回None。换而言之,如果启用了“抑制”功能,就不应依赖返回值。

下面的示例演示了这个类的用法(包括启用和关闭了抑制功能的情形):


>>> calculator = MuffledCalculator()
>>> calculator.calc('10 / 2')
5.0
>>> calculator.calc('10 / 0') # 关闭了抑制功能
Traceback (most recent call last): File "<stdin>", line 1, in ? File "MuffledCalculator.py", line 6, in calc return eval(expr) File "<string>", line 0, in ?
ZeroDivisionError: integer division or modulo by zero
>>> calculator.muffled = True
>>> calculator.calc('10 / 0')
Division by zero is illegal

如你所见,关闭抑制功能时,捕获了异常ZeroDivisionError,但继续向上传播它。
如果无法处理异常,在except子句中使用不带参数的raise通常是不错的选择,但有时你可能想引发别的异常。在这种情况下,导致进入except子句的异常将被作为异常上下文存储起来,并出现在最终的错误消息中,如下所示

>>> try:
... 1/0
... except ZeroDivisionError:
... raise ValueError
...
Traceback (most recent call last): File "<stdin>", line 2, in <module>
ZeroDivisionError: division by zero

在处理上述异常时,引发了另一个异常:

Traceback (most recent call last): File "<stdin>", line 4, in <module>
ValueError

你可使用raise … from …语句来提供自己的异常上下文,也可使用None来禁用上下文。

>>> try:
... 1/0
... except ZeroDivisionError:
... raise ValueError from None
...
Traceback (most recent call last): File "<stdin>", line 4, in <module>
ValueError

8.3.2 多个except子句

如果你运行前一节的程序,并在提示时输入一个非数字值,将引发另一种异常。

Enter the first number: 10
Enter the second number: "Hello, world!"
Traceback (most recent call last): File "exceptions.py", line 4, in ? print(x / y)
TypeError: unsupported operand type(s) for /: 'int' and 'str'

由于该程序中的except子句只捕获ZeroDivisionError异常,这种异常将成为漏网之鱼,导致程序终止。为同时捕获这种异常,可在try/except语句中再添加一个except子句。

try: x = int(input('Enter the first number: ')) y = int(input('Enter the second number: ')) print(x / y)
except ZeroDivisionError: print("The second number can't be zero!")
except TypeError: print("That wasn't a number, was it?")

8.3.3 一箭双雕

如果要使用一个except子句捕获多种异常,可在一个元组中指定这些异常,如下所示:

try: x = int(input('Enter the first number: ')) y = int(input('Enter the second number: ')) print(x / y)
except (ZeroDivisionError, TypeError, NameError): print('Your numbers were bogus ...')

在上述代码中,如果用户输入字符串、其他非数字值或输入的第二个数为零,都将打印同样的错误消息。当然,仅打印错误消息帮助不大。另一种解决方案是不断地要求用户输入数字,直到能够执行除法运算为止,8.3.6节将介如何这样做。
在except子句中,异常两边的圆括号很重要。一种常见的错误是省略这些括号,这可能导致你不想要的结果,其中的原因请参阅下一节

8.3.4 捕获对象

要在except子句中访问异常对象本身,可使用两个而不是一个参数。(请注意,即便是在你捕获多个异常时,也只向except提供了一个参数——一个元组。)需要让程序继续运行并记录错误(可能只是向用户显示)时,这很有用。下面的示例程序打印发生的异常并继续运行:

try: x = int(input('Enter the first number: ')) y = int(input('Enter the second number: ')) print(x / y)
except (ZeroDivisionError, TypeError) as e: print(e)

在这个小程序中,except子句也捕获两种异常,但由于你同时显式地捕获了对象本身,因此可将其打印出来,让用户知道发生了什么情况。8.3.6节将介绍这种技术的另一种更有用的用途。

8.3.5 一网打尽

即使程序处理了好几种异常,还是可能有一些漏网之鱼。例如,对于前面执行除法运算的程序,如果用户在提示时不输入任何内容就按回车键,将出现一条错误消息,还有一些相关问题出在什么地方的信息(栈跟踪),如下所示:

Traceback (most recent call last): ...
ValueError: invalid literal for int() with base 10: ''

这种异常未被try/except语句捕获,这理所当然,因为你没有预测到这种问题,也没有采取相应的措施。在这些情况下,与其使用并非要捕获这些异常的try/except语句将它们隐藏起来,还不如让程序马上崩溃,因为这样你就知道什么地方出了问题。然而,如果你就是要使用一段代码捕获所有的异常,只需在except语句中不指定任何异常类即可。

try: x = int(input('Enter the first number: ')) y = int(input('Enter the second number: ')) print(x / y)
except: print('Something wrong happened ...')

现在,用户想怎么做都可以。

Enter the first number: "This" is *completely* illegal 123
Something wrong happened ...

像这样捕获所有的异常很危险,因为这不仅会隐藏你有心理准备的错误,还会隐藏你没有考虑过的错误。这还将捕获用户使用Ctrl + C终止执行的企图、调用函数sys.exit来终止执行的企图等。在大多数情况下,更好的选择是使用except Exception as e并对异常对象进行检查。这样做将让不是从Exception派生而来的为数不多的异常成为漏网之鱼,其中包括SystemExit和KeyboardInterrupt,因为它们是从BaseException(Exception的超类)派生而来的。

8.3.6 万事大吉时

在有些情况下,在没有出现异常时执行一个代码块很有用。为此,可像条件语句和循环一样,给try/except语句添加一个else子句。

try: print('A simple task')
except: print('What? Something went wrong?')
else: print('Ah ... It went as planned.')

如果你运行这些代码,输出将如下:

A simple task
Ah ... It went as planned.

通过使用else子句,可实现8.3.3节所说的循环。

while True: try: x = int(input('Enter the first number: ')) y = int(input('Enter the second number: ')) value = x / y print('x / y is', value) except: print('Invalid input. Please try again.') else: break

在这里,仅当没有引发异常时,才会跳出循环(这是由else子句中的break语句实现的)。换而言之,只要出现错误,程序就会要求用户提供新的输入。下面是这些代码的运行情况:

Enter the first number: 1
Enter the second number: 0
Invalid input. Please try again.
Enter the first number: 'foo'
Enter the second number: 'bar'
Invalid input. Please try again.
Enter the first number: baz
Invalid input. Please try again.
Enter the first number: 10
Enter the second number: 2
x / y is 5

《Python基础教程(第3版)》笔记:第8章异常相关推荐

  1. python基础教程第4版-Python基础教程(第3版) 笔记(四)

    第二章 列表和元素 2.1 序列概述 列表和元组的主要不同在于,列表是可以修改的,而元组不可以. 创建一个由数据库中所有人员组成的列表: >>> edward = ['Edward ...

  2. python基础教程第三版试题-Python基础教程(第3版) 笔记(二)

    1.8模块 Python提供了完成(某人的年 龄为32.9,并想将这个值向下圆整为32,因为他还没有满33岁)这种任务的函 数floor. 导入模块,可以使用特殊命令import.函数floor包含在 ...

  3. Python基础教程(第3版) 笔记(一)

    1.1 数和表达式: 除法运算的结果为小数,即浮点数 >>>1/2 0.5 除法运算为整数,使用双斜杠 >>>1//2 0 >>>5.0//2.4 ...

  4. python的符号lt和gt怎么输入_lt;lt;Python基础教程gt;gt;学习笔记 | 第12章 | 图形用户界面...

    Python支持的工具包非常多.但没有一个被觉得标准的工具包.用户选择的自由度大些.本章主要介绍最成熟的跨平台工具包wxPython.官方文档: ------ 丰富的平台: Tkinter实际上类似于 ...

  5. python基础教程电子版-Python基础教程(第2版 修订版) pdf

    Python基础教程(第2版 修订版) 目录 D11章快速改造:基础知识1 1.1安装Python1 1.1.1Windows1 1.1.2Linux和UNIX3 1.1.3苹果机(Macintosh ...

  6. Python基础教程(第2版 修订版) pdf

    Python基础教程(第2版 修订版) 目录 D11章快速改造:基础知识1 1.1安装Python1 1.1.1Windows1 1.1.2Linux和UNIX3 1.1.3苹果机(Macintosh ...

  7. 《python基础教程(第二版)》学习笔记 基础部分(第1章)

    <python基础教程(第二版)>学习笔记 基础部分(第1章) python常用的IDE: Windows: IDLE(gui), Eclipse+PyDev; Python(comman ...

  8. Python基础教程(第3版)》笔记:第6章抽象

    Python基础教程(第3版)>笔记:第6章抽象 **斐波那契数列:**每个数都是前两个数的和. fibs = [0,1] for i in range(8):fibs.append(fibs[ ...

  9. Python 基础教程(第二版)读书笔记

    Python 基础教程(第二版) 第一章 在 Python 3 中可直接使用长整数,而不必添加 L 或者 l 的后缀. print在 Python 3 中是函数. 在交互式解释器中使用 if 语句,需 ...

最新文章

  1. java怎么求两组整数的或集,确定整数是否在具有已知值集的两个整数(包括)之间的最快方法...
  2. pod setup慢的解决方法
  3. Hadoop参数汇总
  4. Kotlin中的接口回调
  5. android录音功能的实现
  6. servlet下根据相对路径找资源
  7. cin.ignore()函数的用法
  8. 关于NFSv4服务共享目录里的文件UID和GID显示为nobody的解决方法
  9. mysql day of week_在MySQL中按day_of_week排序
  10. 第一章 SQL Server 2005概述文档信息
  11. 汇编语言INC DEC JGE JG JLE JL JNE JE 都是什么意思
  12. 吞食天地2完全版乱码怎么解决_PDF转Word如何转换?PDF转Word乱码怎么解决?
  13. android获取浏览器cookie,获取浏览器cookie
  14. ps--修饰人像的皮肤瑕疵的步骤
  15. html按钮动态效果,8款超酷而实用的CSS3按钮动画
  16. ubuntuv20启动界面美化_聊一聊我的win10系统美化/使用习惯
  17. 汽车系统安装linux,linux系统不仅是电脑上的系统 更是会车载主流系统
  18. c语言二级指针与二维数组
  19. 网站备案后 换服务器,网站备案后更换服务器
  20. 【充电站】_世俗智慧_哲学智慧_.

热门文章

  1. 局部线性嵌入 (Locally linear embedding-LLE)原理总结
  2. android 点赞源码,【Ctrl.js】微信给最新一条消息点赞源码
  3. 亮度键消失、亮度键失灵且电脑亮度为最大 | 小米 | win11 | 解决办法最最最全合集
  4. Keras的loss_weights和class_weight
  5. macOS Monterey 12.2 (21D49) 虚拟机 IOS 镜像
  6. XCODE性能测试方法
  7. 手机怎么压缩图片?分享一下压缩的好方法
  8. 做PPT只会用黑体和宋体?这些可商用字体瞬间提升你的PPT档次
  9. 七牛 java 加水印_七牛云图片加水印
  10. unnormal C++