Python学习笔记 - 异常处理

news2024/9/22 22:17:43

前言

        为了增强程序的健壮性,计算机程序的编写也需要考虑如何处理一些异常的情况,Python 语言提供了异常处理功能,本博文主要介绍 Python 异常处理机制。

一 异常处理举例

为了学习 Python 异常处理机制,首先看下面进行除法运算的示例。在 Python Shell 中代码如下:

>>> i = input('请输入数字: ')      # --1
请输入数字: 0
>>> print(i)
0
>>> print(5 / int(i))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
>>>

上述代码第1处通过 input() 函数从控制台读取字符串,该函数语法如下:

input([prompt])

prompt 参数是提示字符串,可以省略。

从控制台读取字符串 0 赋值给变量 i,当执行 print(5 / (int)i) 语句时,会抛出 ZeroDivisionError 异常,ZeroDivisionError 是除 0 异常。这是因为在数学上除数不能为 0,所以执行表达式 (5 / (int)a) 会有异常。

重新输入如下字符串:

>>> i = input('请输入数字: ')
请输入数字: QWE
>>> print(i)
QWE
>>> print(5 / int(i))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: 'QWE'
>>>

这次输入的是字符串 QWE,因为它不能转换为整数类型,因此会抛出 ValueError 异常。

程序运行过程中难免会发生异常,发生异常并不可怕,程序员应该考虑到有可能会发生这些异常,编程时应处理这些异常,不能让程序终止,这才是健壮的程序。

二 异常类继承层次

Python 中异常根类是 BaseException,异常类继承层次如下所示:

从异常类的继承层次可见,BaseException 的子类很多,其中 Exception 是非系统退出的异常,它包含了很多常用异常。如果自定义异常需要继承 Exception 及其子类,不要直接继承 BaseException 根类。另外,还有一类异常是 Warning,Warning 是警告,提示程序潜在问题。

提示】从异常类继承的层次可见,Python 中的异常类命名主要是后缀有 Exception、Error 和 Warning,也有少数几个没有采用这几个后缀命名的,当然这些后缀命名的类都有它的含义。但是有些中文资料根据异常类后缀名有时翻译为“异常”,有时翻译为“错误”,为了不引起误会,本博文将它们统一为 “异常”,特殊情况会另加说明。

三 常见异常

Python 有很多异常,熟悉几个常用异常是有必要的。本节就介绍几个常见异常。

3.1 AttributeError 异常

AttributeError 异常试图访问一个类中不存在的成员(包括:成员变量、属性和成员方法)而引发的异常

>>> class Animal(object):    # --1
...     pass
...
>>> a1 = Animal()            # --2
>>> a1.run()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Animal' object has no attribute 'run'
>>> print(a1.age)            # --3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Animal' object has no attribute 'age'
>>> print(Animal.weight)     # --4
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'Animal' has no attribute 'weight'
>>>

(1)上述代码第1处是定义 Animal 类。

(2)代码第2处是试图访问 Animal 类的 run() 方法,由于 Animal 类中没有定义 run() 方法,结果抛出了 AttributeError 异常。

(3)代码第3处是试图访问 Animal 类的实例变量(或属性)age,结果抛出了 AttributeError 异常。

(4)代码第4处是试图访问 Animal 类的类变量 weight,结果抛出了 AttributeError 异常。

3.2 OSError 异常

        OSError 是操作系统相关异常。Python 3.3 版本后 IOError(输入输出异常)也并入到 OSError 异常,所以输出输出异常也属于 OSError 异常。例如,“未找到文件” 或 “磁盘已满” 异常。

在 Python Shell 中执行如下代码:

>>> f = open('abc.txt')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory: 'abc.txt'
>>>

上述代码 f = open('abc.txt') 是打开当前目录下的 abc.txt 文件,由于不存在该文件,所以抛出 FileNotFoundError 异常。FileNotFoundError 属于 OSError 异常,前者是后者的子类。

3.3 IndexError 异常

IndexError 异常是访问序列元素时,下标索引超出取值范围所引发的异常

在 Python Shell 中执行如下代码:

>>> code_list = [125, 56, 89, 36]
>>> code_list[4]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range
>>>

上述代码 code_list[4] 试图访问 code_list 列表的第5个元素,由于 code_list 列表最多只有 4 个元素,所以会引发 IndexError 异常。

3.4 KeyError 异常

KeyError 异常是试图访问字典里不存在的键时而引发的异常

在 Python Shell 中执行如下代码:

>>> dict1 = {101:'aaa', 102:'bbb', 103:'ccc'}
>>> dict1[104]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 104
>>>

上述代码 dict1[104] 试图访问字典 dict1 中键为 104 的值,但 104 键在字典 dict1 中不存在,所以引发了 KeyError 异常。

3.5 NameError 异常

NameError 是试图使用一个不存在的变量而引发的异常

在 Python Shell 中执行如下代码:

>>> value1        # --1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'value1' is not defined
>>> a = value1    # --2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'value1' is not defined
>>> value1 = 10   # --3
>>> print(value1) # --4
10
>>>

上述代码第1处和第2处都是读取 value1 变量值,由于之前没有创建过 value1,所以会引发 NameError 异常。但是代码第3处 value1 = 10 却不会引发异常,那是因为赋值时,如果变量 value1 不存在就会创建它,所以不会引发异常。代码第4处打印出了变量 value1 的值。

3.6 TypeError 异常

TypeError 是试图传入变量类型与要求不符合时而引发的异常

在 Python Shell 中执行如下代码:

>>> i = '2'
>>> print(5 / i)    # --1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for /: 'int' and 'str'
>>>

上述代码第1处 (5 / i) 表达式进行除法运算,需要变量 i 是一个数字类型,然而传入的却是一个字符串类型,所以引发 TypeError 异常。

3.7 ValueError 异常

ValueError 异常是由于传入一个无效的参数值而引发的异常。在 Python Shell 中执行如下代码:

>>> i = 'QWE'
>>> print (5 / int(i))    # --1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: 'QWE'
>>>

上述代码第1处 (5 / int(i)) 表达式进行除法运算,需要传入的变量 i 是能够使用 int() 函数转换为数字的参数。然而传入的字符串 'QWE' 不能转换成数字,所以引发了 ValueError 异常。

四 捕获异常

        Python 对待异常的处理方式是:当前函数有能力解决,则捕获异常后自行进行处理;如果没有能力处理,则抛给上层调用者(函数)进行处理。如果上层调用者还无力解决,则继续抛给它的上层调用者。异常就是这样向上传递直到有函数处理它。如果所有的函数都没有处理该异常,那么 Python 解释器会终止程序运行。这就是 Python 的异常传播过程。

4.1 try-except 语句

捕获异常是通过 try-except 语句实现的,最基本的 try-except 语句语法如下:

try:
    <可能会抛出异常的语句>
except [异常类型]:
    <处理异常语句>

1)try 代码块

try 代码块中包含执行过程中可能会抛出异常的语句。

2)except 代码块

每个 try 代码块可能伴随一个或多个 except 代码块,用于处理 try 代码块中所有可能抛出的多种异常。except 语句中如果省略 “异常类型”,则会捕获所有类型的异常;如果指定具体类型的异常,则会捕获该类型异常,以及它的子类型异常

示例:一个 try-except 示例程序。

# coding=utf-8
# 代码文件:捕获异常示例/try_except.py

import datetime as dt      # --1

def read_date(in_date):    # --2
    try:
        date = dt.datetime.strptime(in_date, '%Y-%m-%d') # --3
        return date
    except ValueError as e:     # --4
        print('处理 ValueError 异常')
        print(e)

str_date = '2022-10-15'
# str_date = '202B-10-15'
print('日期 = {0}'.format(read_date(str_date)))

上述代码第 1 处导入了 datetime 模块,datetime 是 Python 内置的日期时间模块。代码第 2 处定义了一个函数,在函数中将传入的字符串转换为日期,并进行格式化。但并非所有的字符串都是有效的日期字符串,因此调用代码第3处的 strptime() 方法有可能抛出 ValueError 异常。代码第4处是捕获 ValueError 异常。本示例中的 '2022-10-15' 字符串是有效的日期字符串,因此不会抛出异常。如果将字符串改为无效的日期字符串,如 '202B-10-15',则会打印以下信息。

> python try_except.py
处理ValueError异常
time data '202B-10-15' does not match format '%Y-%m-%d'
日期 = None

其中,ValueError as e 中的 e 是异常对象,print(e) 语句可以打印异常对象,打印异常对象会输出如下的异常信息。

time data '202B-10-15' does not match format '%Y-%m-%d'

 4.2 多 except 代码块

如果 try 代码块中有很多语句抛出异常,而且抛出的异常种类有很多,那么可以在 try 后面跟有多个 except 代码块。多 except 代码块语法如下:

try:
    <可能会抛出异常的语句>
except [异常类型1]:
    <处理异常语句>
except [异常类型2]:
    <处理异常语句>
...
except [异常类型n]:
    <处理异常语句>

在多个 except 代码块情况下,当一个 except 代码块捕获到一个异常时,其他的 except 代码块就不再进行匹配。

注意:当捕获的多个异常类之间存在父子关系时,捕获异常顺序与 except 代码块的顺序有关。从上到下先捕获子类,后捕获父类,否则子类捕获不到。

示例代码如下:

# coding=utf-8
# 代码文件:异常处理/try_except2.py
# 多except代码块示例

import datetime as dt

def read_date_from_file(filename):  # --1
    try:
        file = open(filename)       # --2
        in_date = file.read()       # --3
        in_date = in_date.strip()   # --4
        date = dt.datetime.strptime(in_date, '%Y-%m-%d') # --5
        return date
    except ValueError as e:         # --6
        print('处理 ValueError 异常')
        print(e)
    except FileNotFoundError as e:  # --7
        print('处理 FileNotFountError 异常')
        print(e)
    except OSError as e:            # --8
        print('处理 OSError 异常')
        print(e)

date = read_date_from_file('readme.txt')
print('日期 = {0}'.format(date))

         上述代码通过 open() 函数从文件 read.txt 中读取字符串,然后解析成为日期。在调用 open() 函数时是有可能会抛出异常的,所以将其放在 try 代码块中。

        在 try 代码块中,代码第 1 处定义函数 read_date_from_file(filename) 用来从文件中读取字符串,并解析成为日期。代码第2处调用 open() 函数读取文件,它有可能抛出 FileNotFoundError 异常等 OSError 异常。如果抛出 FileNotFoundError 异常,则会被代码第 7 处的 except 捕获。如果抛出 OSError 异常,则会被代码第 8 处的 except 捕获。代码第 3 处 file.read() 方法是从文件中读取数据,它也可能抛出 OSError 异常。如果抛出 OSError 异常,则会被代码第8处的 except 捕获。代码第 4 处的 in_date.strip() 方法是剔除字符串前后空白字符(包括空格、制表符、换行和回车等字符)。代码第5处 strptime() 方法可能抛出 ValueError 异常。如果抛出异常,则会被代码第6处的 except 捕获。

        如果将 FileNotFoundError 和 OSError 捕获顺序调换,代码如下:

def read_date_from_file(filename):
    try:
        file = open(filename)
        in_date = file.read()
        in_date = in_date.strip()
        date = dt.datetime.strptime(in_date, '%Y-%m-%d')
        return date
    except ValueError as e:
        print('处理 ValueError 异常')
        print(e)
    except OSError as e:
        print('处理 OSError 异常')
        print(e)
    except FileNotFoundError as e:
        print('处理 FileNotFountError 异常')
        print(e)

        那么 except FileNotFoundError as e 代码块永远不会进入,FileNotFoundError 异常代码永远不会被执行。因为 OSError 是 FileNotFoundError 父类,而 ValueError 异常与 OSError 和 FileNotFoundError 异常没有父子关系,捕获 ValueError 异常位置可以随意放置。

4.3 try-except 语句嵌套

Python 提供的 try-except 语句是可以任意嵌套的,修改 4.2 节示例代码如下:

# coding=utf-8
# 代码文件:异常处理/try_except3.py
# try-except语句嵌套示例

import datetime as dt

def read_date_from_file(filename):
    try:
        file = open(filename)
        try:                           # --1
            in_date = file.read()      # --2
            in_date = in_date.strip()
            date = dt.datetime.strptime(in_date, '%Y-%m-%d') # --3
            return date
        except ValueError as e:        # --4
            print('处理 ValueError 异常')
            print(e)
    except FileNotFoundError as e:     # --5
        print('处理 FileNotFoundError 异常')
        print(e)
    except OSError as e:               # --6
        print('处理 OSError 异常')
        print(e)

date = read_date_from_file('readme.txt')
print('日期 = {0}'.format(date))

        上述代码第1~4处是捕获 ValueError 异常的 try-except 语句,可见这个 try-except 语句就嵌套在捕获 FileNotFoundError 和 OSError 异常的 try-except 语句内。

        程序执行时如果内层抛出异常,首先由内层的 except 进行捕获,如果捕获不到,则由外层的 except 捕获。例如,代码第 2 处的 read() 方法可能抛出 OSError 异常,该异常无法被内层 except 捕获,最后被代码第 6 处的外层 except 捕获。

注意:try-except 代码块不仅可以嵌套在 try 代码块中,还可以嵌套在 except 代码块或 finally 代码块中。try-except 嵌套会使程序流程变得复杂,如果能用多 except 捕获的异常,尽量不要使用 try-except 嵌套。要梳理好程序的流程再考虑 try-except 嵌套的必要性。

4.4 多重异常捕获

多个 except 代码块客观上提高了程序的健壮性,但是也大大增加了程序代码量。有些异常虽然种类不同,但捕获之后的处理却是相同的,看如下代码:

try:
    <可能会抛出异常的语句>
except ValueError as e:
    <调用方法 method1 处理>
except OSError as e:
    <调用方法 method1 处理>
except FileNotFoundError as e:
    <调用方法 method1 处理>

        三个不同类型的异常,要求捕获之后的处理都是调用 method1 方法。是否可以把这些异常合并起来处理呢?Python 中可以把这些异常放在一个元组中,这就是多重异常捕获,可以帮助解决此类问题。上述代码修改如下:

# coding=utf-8
# 代码文件:异常处理/try_except4.py
# try-except语句多重异常捕获示例

import datetime as dt

def read_date_from_file(filename):
    try:
        file = open(filename)
        in_date = file.read()
        in_date = in_date.strip()
        date = dt.datetime.strptime(in_date, '%Y-%m-%d')
        return date
    except (ValueError, OSError) as e:         # --1
        print('调用方法 method1 异常处理...')
        print(e)

date = read_date_from_file('readme.txt')
print('日期 = {0}'.format(date))

代码中 (ValueError, OSError) 就是多重异常捕获。

注意:可能有人会问为什么不写成 (ValueError, FileNotFoundError, OSError) 呢?这是因为 FileNotFoundError 属于 OSError 异常,OSError 异常可以捕获它的所有子类异常了。

五 异常堆栈跟踪

有时从程序员的角度需要知道更加详细的异常信息时,可以打印堆栈跟踪信息。堆栈跟踪信息可以通过 Python 内置模块 traceback 提供的 print_exc() 函数实现,print_exc() 函数的语法格式如下:

traceback.print_exc(limit=None, file=None, chain=True)

其中,参数 limit 限制堆栈跟踪个数,默认 None 是不限制;参数 file 判断是否输出堆栈跟踪信息到文件,默认 None 是不输出到文件;参数 chain 为 True,则将 __cause____context__ 等属性串联起来,就像解释器本身打印未处理异常一样打印。

堆栈跟踪示例代码如下:

# coding=utf-8
# 代码文件:异常处理/traceback_demo.py
# 异常堆栈跟踪

import datetime as dt
import traceback as tb  # --1

def read_date_from_file(filename):
    try:
        file = open(filename)
        in_date = file.read()
        in_date = in_date.strip()
        date = dt.datetime.strptime(in_date, '%Y-%m-%d')
        return date
    except (ValueError, OSError) as e:
        print('调用方法 method1 异常处理...')
        tb.print_exc()  # --2

date = read_date_from_file('readme.txt')
print('日期 = {0}'.format(date))

上述代码第 2 处 tb.print_exc() 语句是打印异常堆栈信息,tb.print_exc() 函数使用了 traceback 模块,因此需要在文件开始导入 traceback 模块。

发生异常输出结果如下:

F:\python_work\异常处理>python traceback_demo.py
调用方法 method1 异常处理...
Traceback (most recent call last):
  File "F:\python_work\异常处理\traceback_demo.py", line 13, in read_date_from_file
    date = dt.datetime.strptime(in_date, '%Y-%m-%d')
  File "D:\Program Files\Python\lib\_strptime.py", line 568, in _strptime_datetime
    tt, fraction, gmtoff_fraction = _strptime(data_string, format)
  File "D:\Program Files\Python\lib\_strptime.py", line 349, in _strptime
    raise ValueError("time data %r does not match format %r" %
ValueError: time data '202b-10-30' does not match format '%Y-%m-%d'
日期 = None

堆栈信息从上往下为程序执行过程中函数(或方法)的调用顺序,其中的每一条信息明确指出了哪一个文件、哪一行调用哪个函数或方法。程序员能够通过堆栈信息很快定位程序哪里出了问题。

提示:在捕获到异常之后,通过 print_exc() 函数可以打印异常堆栈跟踪信息,往往只是用于调试,给程序员提示信息。堆栈跟踪信息对最终用户是没有意义的,本例中如果出现异常很有可能是用户输入的日期无效。捕获到异常之后应该给用户弹出一个对话框,提示用户输入日期无效,请用户重新输入,用户重新输入之后再重新调用上述函数。这才是捕获异常之后的正确处理方案。

六 释放资源

        有时 try-except 语句会占用一些系统资源,如打开文件、网络连接、连接数据库和使用数据结果等,这些资源不能通过 Python 的垃圾回收机制回收,需要程序员自己手动释放。为了确保这些资源能够被释放,可以使用 finally 代码块或 with as 自动资源管理。

6.1 finally 代码块

try-except 语句后面还可以跟一个 finally 代码块,try-except-finally 语句语法如下:

try:
    <可能会抛出异常的语句>
except [异常类型1]:
    <处理异常>
except [异常类型1]:
    <处理异常>
...
except [异常类型n]:
    <处理异常>
finally:
    <释放资源>

无论 try 正常结束还是 except 异常结束都会执行 finally 代码块,如下图所示:

图1  finally代码块流程

实例:使用 finally 代码块示例代码如下:

# coding=utf-8
# 代码文件:异常处理/try_except_finally.py
# 异常堆栈跟踪

import datetime as dt

def read_date_from_file(filename):
    try:
        file = open(filename)
        in_date = file.read()
        in_date = in_date.strip()
        date = dt.datetime.strptime(in_date, '%Y-%m-%d')
        return date
    except ValueError as e:
        print('处理 ValueError 异常')
    except FileNotFoundError as e:
        print('处理 FileNotFoundError 异常')
    except OSError as e:
        print('处理 OSError 异常')
    finally:          # --1
        file.close()  # --2

date = read_date_from_file('readme.txt')
print('日期 = {0}'.format(date))

上述代码第1处是 finally 代码块,在这里通过关闭文件释放资源,即代码第2处:file.close() 的关闭文件语句。上述代码还是存在问题,如果在执行 open(filename) 打开文件时,即使抛出了异常也会执行 finally 代码块。执行 file.close() 关闭文件会抛出如下异常。

F:\python_work\异常处理>python try_except_finally.py
处理 FileNotFoundError 异常
Traceback (most recent call last):
  File "F:\python_work\异常处理\try_except_finally.py", line 23, in <module>
    date = read_date_from_file('readme.txt')
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "F:\python_work\异常处理\try_except_finally.py", line 21, in read_date_from_file
    file.close()  # --2
    ^^^^
UnboundLocalError: cannot access local variable 'file' where it is not associated with a value

UnboundLocalError 异常是 NameError 异常的子类,异常信息提示没有找到 file 变量,这是因为 open(filename) 打开文件失败,所以 file 变量没有被创建。事实上 file.close() 关闭的前提是文件已经成功打开。为了解决此问题,可以使用 else 代码块。

6.2 else 代码块

与 while 和 for 循环类型,try 语句也可以带有 else 代码块,它是在程序正常结束时执行的代码块,程序流程如下图所示:

图2  else 代码块流程

将 6.1 节 的示例程序中存在的问题可以使用 else 代码块解决,修改 6.1 示例代码如下:

# coding=utf-8
# 代码文件:异常处理/try_except_else.py
# 异常处理else代码块

import datetime as dt

def read_date_from_file(filename):
    try:
        file = open(filename)  # --1
    except OSError as e:
        print('处理 OSError 异常')
        print('打开文件失败')
    else:                      # --2
        print('打开文件成功')
        try:
            in_date = file.read()
            in_date = in_date.strip()
            date = dt.datetime.strptime(in_date, '%Y-%m-%d')
            return date
        except ValueError as e:
            print('处理 ValueError 异常')
        except OSError as e:
            print('处理 OSError 异常')
        finally:               # --3
            file.close()

date = read_date_from_file('readme.txt')
print('日期 = {0}'.format(date))

上述代码中 open(filename) 语句单独放在一个 try 中,见代码第1处。如果正常打开这个文件,程序会执行 else 代码块,见代码第2处。else 代码块中嵌套了 try 语句,在这个 try 代码中读取文件内容和解析日期,最后在嵌套 try 对应的 finally 代码块中执行 file.close() 关闭文件。

1)运行结果1:readme.txt 文件不存在时

F:\python_work\异常处理>python try_except_else.py
处理 OSError 异常
打开文件失败
日期 = None

2)运行结果2:readme.txt 文件存在,文件内容为: 2022-12-04

F:\python_work\异常处理>python try_except_else.py
打开文件成功
日期 = 2022-12-04 00:00:00

3)运行结果3:readme.txt 文件存在,文件内容为:2022-12-0B

F:\python_work\异常处理>python try_except_else.py
打开文件成功
处理 ValueError 异常
日期 = None

6.3 with as 代码块自动资源管理

6.2 节的程序代码虽然 “健壮”,但是程序流程比较复杂,这样的程序代码难以维护。为此 Python 提供了一个 with as 代码块帮助自动释放资源,它可以替代 finally 代码块,优化代码结构,提供程序的可读性。with as 提供了一个代码块,在 as 后面声明一个资源变量,当 with as 代码块结束之后自动释放资源。

实例:示例代码如下。

# coding=utf-8
# 代码文件:异常处理/try_with_as.py
# 异常处理 with as 代码块自动资源管理

import datetime as dt

def read_date_from_file(filename):
    try:
        with open(filename) as file:  # --1
            in_date = file.read()
        in_date = in_date.strip()
        date = dt.datetime.strptime(in_date, '%Y-%m-%d')
        return date
    except ValueError as e:
        print('处理 ValueError 异常')
    except OSError as e:
        print('处理 OSError 异常')
        print('打开文件失败')

date = read_date_from_file('readme.txt')
print('日期 = {0}'.format(date))

上述代码第1处是使用 with as 代码块,with 语句后面的 open(filename) 语句可以创建资源对象,然后赋值给 as 后面的 file 变量。在 with as 代码块中包含了资源对象相关代码。完成后自动释放。采用了自动资源管理后不再需要 finally 代码块,不需要自己释放这些资源。

注意:所有可以自动管理的资源,需要实现上下文管理协议(Context Management Protocol)。

示例运行结果:readme.txt 文件存在,文件内容为:2022-12-0B

F:\python_work\异常处理>python try_with_as.py
处理 ValueError 异常
日期 = None

七 自定义异常类

有些公司为了提供代码的可重用性,自己开发了一些 Python 类库,其中有自己编写的一些异常类。实现自定义异常类需要继承 Exception 类或其子类。

实现自定义异常类示例代码如下:

# coding=utf-8
# 代码文件: 异常处理/custom_except_class.py

class MyException(Exception):
    def __init__(self, message):   # --1
        super().__init__(message)  # --2

上述代码实现了自定义异常类,代码第1处是定义构造方法类,其中的参数 message 是异常描述信息。代码第2处 super().__init__(message) 是调用父类构造方法,并把参数 message 传入给父类构造方法。自定义异常就是这样简单,只需要提供一个字符串参数的构造方法就可以了。

8 显式抛出异常

上文中见到的异常都是由系统生成的,当异常抛出时,系统会创建一个异常对象,并将其抛出。但也可以通过 raise 语句显式抛出异常,语法格式如下:

raise BaseException 或其子类的实例

显式抛出异常的目的有很多,例如不想某些异常传给上层调用者,可以捕获之后重新显式抛出另一种异常给调用者。

修改第 4 节示例代码如下:

# coding=utf-8
# 代码文件:异常处理/try_except_raise.py
# 使用 raise 语句显示抛出异常

import datetime as dt

class MyException(Exception):
    def __init__(self, message):
        super().__init__(message)

def read_date_from_file(filename):
    try:
        file = open(filename)
        in_date = file.read()
        in_date = in_date.strip()
        date = dt.datetime.strptime(in_date, '%Y-%m-%d')
        return date
    except ValueError as e:
        raise MyException('不是有效的日期')     # --1
    except FileNotFoundError as e:
        raise MyException('文件找不到')       # --2
    except OSError as e:
        raise MyException('文件无法打开或无法读取') # --3

date = read_date_from_file('readme.txt')
print('日期 = {0}'.format(date))

如果软件设计者不希望 read_date_from_file() 中捕获的 ValueErrorFileNotFoundErrorOSError 异常出现在上层调用者中,那么可以在捕获到这些异常时,通过 raise 语句显式抛出一个异常,见代码第1、2 和 3 处显式抛出抛出自定义的 MyException 异常。

注意:raise 显式抛出的异常与系统生成并抛出的异常在处理方式上没有区别,就是两种方法,要么捕获后自己处理,要么抛出给上层调用者。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/61079.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL的主从复制与读写分离详解

MySQL的主从复制与读写分离详解读写分离概述什么是读写分离为什么要读写分离什么时候要读写分离MySQL主从复制与读写分离主从复制的概念MySQL支持的复制类型主从复制的工作过程主从复制示例保证MySQL主从服务器时间同步主节点服务器配置从节点服务器设置验证MySQL读写分离MySQL…

Vue3 学习笔记 —— Hooks、全局函数和变量、Vue3 插件

目录 1. Hooks 1.1 Vue2 中的 mixins 1.1.1 mixins 是什么&#xff1f; 1.1.2 mixins 缺点&#xff1f; 1.2 Vue3 中的 Hooks 1.2.1 Vue3 Hooks 是什么&#xff1f; 1.2.2 Vue3 内置 hooks 举例 1.2.3 自定义 Hooks 2. 全局函数和全局变量 2.1 app.config.globalProp…

【深入设计模式】适配器模式—一切皆有可能

文章目录1. 适配器模式1.1 适配器模式简介1.2 适配器模式结构1.3 适配器模式示例2. 适配器模式在源码中的应用3. 总结适配器这个词来源于硬件领域&#xff0c;是一个独立的硬件设备接口&#xff0c;允许硬件或电子接口与其它硬件或电子接口相连&#xff0c;比如常见的电源适配器…

(附源码)SSM芜湖公共书房服务平台 毕业设计 250859

SSM芜湖公共书房服务平台 摘 要 一座有底蕴的城市&#xff0c;应该是一个阅读的城市&#xff1b;一个有魅力的城市&#xff0c;应该是一个散发着书香的城市&#xff0c;而全民阅读量逐年增加&#xff0c;是社会进步、文明程度提高的重要标志。各大城市启动“全民阅读”工作以来…

Redis缓存过期和和内存淘汰策略

目录 1、MaxMemory 2、Expire数据结构 3、删除策略 3.1、惰性删除 3.2、主动删除 3.3、缓存淘汰策略 ​​​​​​​3.4、缓存淘汰策略的选择 1、MaxMemory Redis作为DB使用时&#xff0c;为了保证数据的完整性&#xff0c;不允许淘汰任何键值对。Redis作为缓存使用时&…

汇编语言与微机原理 期末半开卷复习整理(上)

8086CPU寄存器 8086&#xff1a;16位&#xff0c;4.77MHz~10MHz,16根数据线&#xff0c;20根地址线 AX/AL:乘除法指令中用作累加器&#xff0c;IO指令中用作数据寄存器&#xff0c;可显式或隐含调用 AH&#xff1a;在LAHF用作目的寄存器&#xff0c;隐含调用。 AL&#xff1a;…

大数据必学Java基础(一百一十一):过滤器注解应用和开发案例

文章目录 过滤器注解应用和开发案例 一、过滤器注解应用 二、开发案例

Python学习日记-第三十八天-生成器(第二节)

系列文章目录 使用greenlet&#xff0c;gevent完成多任务一、使用greenlet&#xff0c;gevent完成多任务 这里要先在pycharm里面提前安装好greenlet和gevent的包 操作&#xff1a; 代码&#xff1a; from greenlet import greenlet import timedef test1():while True:prin…

Android入门第39天-系统设置Configuration类

简介 本节给大家介绍的Configuration类是用来描述手机设备的配置信息的&#xff0c;比如屏幕方向&#xff0c; 触摸屏的触摸方式等。 Configuration给我们提供的方法列表 densityDpi&#xff1a;屏幕密度fontScale&#xff1a;当前用户设置的字体的缩放因子hardKeyboardHidd…

[深度学习] python基础支持汇总

这个系列放一些看神经网络源码过程中的python语法现象 文章目录前言一、list操作://extends/append的区别1.引入2.细致场景再现前言 例如&#xff1a;这个系列放一些看神经网络源码过程中的python语法现象, 直接解析语法太干瘪无聊.希望用这个方式来巩固所学知识 一、list操作…

电商评论文本情感分类(中文文本分类+中文词云图)(第一部分-textcnn)

电商评论文本情感分类(中文文本分类中文词云图) 第一部分 第二部分Bert部分 本项目包含&#xff1a; 1.中文文本处理 2.中文词云图绘制 3.中文词嵌入 4.基于textcnn的中文文本分类&#xff08;Test_Acc89.2000&#xff09; 5.基于bert的中文文本分类&#xff08;Test_Acc0.…

ServiceComb场景及其原理

文章目录Java-ChassisEnableServiceComb初始化SCBSPIServiceUtils自定义SPI加载器职责链管理器FilterChainsManager/ConsumerHandlerManagerRpcSchema注册服务如何保活&#xff1f;RpcReferencePropertySourcesPlaceholderConfigurerThreadPoolExecutorEx/LinkedBlockingQueueE…

深刻理解JAVA并发中的有序性问题和解决之道

问题 Java并发情况下总是会遇到各种意向不到的问题&#xff0c;比如下面的代码&#xff1a; int num 0;boolean ready false; // 线程1 执行此方法 public void actor1(I_Result r) {if(ready) {r.r1 num num;} else {r.r1 1;} } // 线程2 执行此方法 public void actor…

Clickhouse 使用DBeaver连接

ClickHouse是一个用于联机分析(OLAP)的列式数据库管理系统(DBMS)。 据处理大致可以分成两大类&#xff1a;联机事务处理OLTP&#xff08;on-line transaction processing&#xff09;、联机分析处理OLAP&#xff08;On-Line Analytical Processing&#xff09;。 OLTP是传统的…

JavaWeb--JDBC核心技术

JavaWeb--JDBC核心技术JDBC核心技术第1章&#xff1a;JDBC概述1.1 数据的持久化1.2 Java中的数据存储技术1.3 JDBC介绍1.4 JDBC体系结构1.5 JDBC程序编写步骤第2章&#xff1a;获取数据库连接2.1 要素一&#xff1a;Driver接口实现类2.1.1 Driver接口介绍2.1.2 加载与注册JDBC驱…

Redis学习笔记(六)

哨兵 哨兵时一个分布式系统&#xff0c;用于对主从结构中的每台服务器进行监控&#xff0c;当出现故障时通过投票机制选择新的master&#xff0c;并将所有slave连接到新的master哨兵的作用 监控 不断检查master和slave是否正常运行master存活检测、master与slave运行情况检测 通…

Linux调度(三)——抢占式调度

目录 抢占式场景一&#xff1a; 抢占式场景二 抢占的时机 用户态的抢占时机 抢占式机一&#xff1a; 抢占时机二&#xff1a; 内核态的抢占时机 时机一 时机二 总结 之前讲了主动式调度&#xff0c;就是进程运行到一半&#xff0c;因为等待I/O等操作而主动让出CPU&a…

动态规划算法(3)(不同方案数问题+拆分问题)

文章目录不同路径不同路径II整数拆分不同的二叉搜索树动态规划解题五步走&#xff1a; 确定dp数组以及下标的含义确定递推公式dp数组如何初始化确定遍历顺序举例推导dp数组 不同路径 力扣传送门&#xff1a; https://leetcode.cn/problems/unique-paths/description/ 确定dp…

[附源码]JAVA毕业设计酒店订房系统(系统+LW)

[附源码]JAVA毕业设计酒店订房系统&#xff08;系统LW&#xff09; 目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&…

基于opencv答题卡识别基本处理_1

文章目录1.读取图片2.图片预处理2.1 原图转换为灰度图2.2 高斯滤波处理&#xff0c;去除噪声点2.3 增强亮度2.4 自适应二值化2.5 图片可视化3. 添加边框3.1 使用copyMakeBorder添加边框3.2 可视化图片查看效果3.3 手动截取答题卡区域1.读取图片 img cv2.imread(images/5.png)…