Python 异常处理详解 [Python 异常][获取异常信息的方法][try except 详解][try except else 详解][try except finally 详解]

您的“关注”和“点赞”,是信任,是认可,是支持,是动力…

如意见相佐,可留言。
本人必将竭尽全力试图做到准确和全面,终其一生进行修改补充更新。

1 try except 详解

1.1 try except 概述

Python 中,用 try except 语句块捕获并处理异常。

当程序发生不同的意外情况时,会对应特定的异常类型,Python 解释器会根据该异常类型选择对应的 except 块来处理该异常。

注意:不管程序代码块是否处于 try 块中,甚至包括 except 块中的代码,只要执行该代码块时出现了异常,系统都会自动生成对应类型的异常。但是,如果此段程序没有用 try 包裹,又或者没有为该异常配置处理它的 except 块,则 Python 解释器将无法处理,程序就会停止运行;如果程序发生的异常经 try 捕获并由 except 处理完成,则程序可以继续执行。

基本语法结构如下所示:

try:
    代码块1  # 可能产生异常的代码块,有且仅有一个
except [(Error1, Error2, ... ) [as e]]:
    代码块2  # 处理异常的代码
except [(Error3, Error4, ... ) [as e]]:
    代码块3  # 处理异常的代码
...  # 可以有多个 except 块
except [Exception]:
    代码块4  # 处理其他异常的代码

对以上格式说明,

  • []:中括号括起来的为可选内容,可有可无。
  • (Error1, Error2,...) 、(Error3, Error4,...):都是具体的异常类型。一个 except 块可以同时处理多种异常。
  • [as e]:表示给异常类型起一个别名 e,这样做的好处是方便在 except 块中调用异常类型。
  • [Exception]:可以表示程序可能发生的所有异常情况,其通常用在最后一个 except 块。
  • try except 语句的执行流程如下所示,
    (1)捕获异常:首先执行 try 中的代码块,如果执行过程中出现异常,系统会自动生成一个异常类型,并将该异常提交给 Python 解释器,此过程称为捕获异常。
    (2)处理异常:当 Python 解释器收到异常对象时,会寻找能处理该异常对象的 except 块,如果找到合适的 except 块,则把该异常对象交给该 except 块处理,这个过程被称为处理异常。
    (3)终止程序:如果 Python 解释器找不到处理异常的 except 块,则程序运行终止,Python 解释器也将退出。

举例如下所示:

例一,正常情况

try:
    # 可能出现异常的代码块
    a = int(input("请输入被除数:"))
    b = int(input("请输入除数:"))
    c = a / b
    print("相除的商为:", c)
except (ValueError, ArithmeticError):  # ZeroDivisionError 是 ArithmeticError 的子类
    # 处理异常的代码块
    print("程序发生了数字格式异常、算术异常之一")
except :  # 未指定具体要捕获的异常类型,这种省略异常类的 except 语句也是合法的,它表示可捕获所有类型的异常,一般会作为异常捕获的最后一个 except 块。
    print("未知异常")
# try except 外的代码
print("程序继续运行")

运行结果:

请输入被除数:6
请输入除数:2
相除的商为: 3.0
程序继续运行

例二,触发异常

try:
    # 可能出现异常的代码块
    a = int(input("请输入被除数:"))
    b = int(input("请输入除数:"))
    c = a / b
    print("相除的商为:", c)
except (ValueError, ArithmeticError):  # ZeroDivisionError 是 ArithmeticError 的子类
    # 处理异常的代码块
    print("程序发生了数字格式异常、算术异常之一")
except :  # 未指定具体要捕获的异常类型,这种省略异常类的 except 语句也是合法的,它表示可捕获所有类型的异常,一般会作为异常捕获的最后一个 except 块。
    print("未知异常")
# try except 外的代码
print("程序继续运行")

运行结果:

请输入被除数:阿杰
程序发生了数字格式异常、算术异常之一
程序继续运行

例三,触发异常

try:
    # 可能出现异常的代码块
    a = int(input("请输入被除数:"))
    b = int(input("请输入除数:"))
    c = a / b
    print("相除的商为:", c)
except (ValueError, ArithmeticError):  # ZeroDivisionError 是 ArithmeticError 的子类
    # 处理异常的代码块
    print("程序发生了数字格式异常、算术异常之一")
except :  # 未指定具体要捕获的异常类型,这种省略异常类的 except 语句也是合法的,它表示可捕获所有类型的异常,一般会作为异常捕获的最后一个 except 块。
    print("未知异常")
# try except 外的代码
print("程序继续运行")

运行结果:

请输入被除数:6
请输入除数:0
程序发生了数字格式异常、算术异常之一
程序继续运行

1.2 获取异常信息的方式

(1)通过异常类提供的属性和方法获取异常信息

每种异常类型都提供了如下几个属性和方法,通过调用它们,就可以获取当前处理异常类型的相关信息:

  • args:返回异常的错误编号和描述字符串;
  • str(e):返回异常信息,但不包括异常信息的类型;
  • repr(e):返回较全的异常信息,包括异常信息的类型。

举例如下所示:

在例二的基础上,添加获取异常信息的代码

try:
    # 可能出现异常的代码块
    a = int(input("请输入被除数:"))
    b = int(input("请输入除数:"))
    c = a / b
    print("相除的商为:", c)
except (ValueError, ArithmeticError) as e:  # ZeroDivisionError 是 ArithmeticError 的子类
    # 处理异常的代码块
    print("程序发生了数字格式异常、算术异常之一")
    print('通过 e.args 获取异常信息为:', e.args)
    print('通过 str(e) 获取异常信息为:', str(e))
    print('通过 repr(e) 获取异常信息为:', repr(e))
except :  # 未指定具体要捕获的异常类型,这种省略异常类的 except 语句也是合法的,它表示可捕获所有类型的异常,一般会作为异常捕获的最后一个 except 块。
    print("未知异常")
# try except 外的代码
print("程序继续运行")

运行结果:

请输入被除数:阿杰
程序发生了数字格式异常、算术异常之一
通过 e.args 获取异常信息为: ("invalid literal for int() with base 10: '阿杰'",)
通过 str(e) 获取异常信息为: invalid literal for int() with base 10: '阿杰'
通过 repr(e) 获取异常信息为: ValueError("invalid literal for int() with base 10: '阿杰'")
程序继续运行

在例三的基础上,添加获取异常信息的代码

try:
    # 可能出现异常的代码块
    a = int(input("请输入被除数:"))
    b = int(input("请输入除数:"))
    c = a / b
    print("相除的商为:", c)
except (ValueError, ArithmeticError) as e:  # ZeroDivisionError 是 ArithmeticError 的子类
    # 处理异常的代码块
    print("程序发生了数字格式异常、算术异常之一")
    print('通过 e.args 获取异常信息为:', e.args)
    print('通过 str(e) 获取异常信息为:', str(e))
    print('通过 repr(e) 获取异常信息为:', repr(e))
except :  # 未指定具体要捕获的异常类型,这种省略异常类的 except 语句也是合法的,它表示可捕获所有类型的异常,一般会作为异常捕获的最后一个 except 块。
    print("未知异常")
# try except 外的代码
print("程序继续运行")

运行结果:

请输入被除数:6
请输入除数:0
程序发生了数字格式异常、算术异常之一
通过 e.args 获取异常信息为: ('division by zero',)
通过 str(e) 获取异常信息为: division by zero
通过 repr(e) 获取异常信息为: ZeroDivisionError('division by zero')
程序继续运行

(2)使用 Python sys.exc_info() 方法获取异常信息

sys 模块中,有两个方法可以返回异常的全部信息,分别是 exc_info()last_traceback(),这两个函数有相同的功能和用法。

exc_info()方法会将当前的异常信息以元组的形式返回,该元组中包含 3 个元素,分别为 type、value 和 traceback,它们的含义分别如下所示:

  • type:异常类型的名称,它是 BaseException 的子类。
  • value:捕获到的异常实例。
  • traceback:是一个 traceback 对象。

举例如下所示:

import sys  # 导入 sys 模块

def demo1(a, b):
        c = a / b
        print("相除的商为:", c)


def demo2(a, b):
    demo1(a, b)


def demo3(a, b):
    demo2(a, b)


try:
    a = int(input("请输入被除数:"))
    b = int(input("请输入除数:"))
    demo3(a, b)
except:  # 未指定具体要捕获的异常类型,这种省略异常类的 except 语句也是合法的,它表示可捕获所有类型的异常,一般会作为异常捕获的最后一个 except 块。
    print(sys.exc_info())

运行结果:

请输入被除数:6
请输入除数:0
(<class 'ZeroDivisionError'>, ZeroDivisionError('division by zero'), <traceback object at 0x000002626F40CE00>)

无法直接看出有关异常的信息,还需要对 traceback 对象做进一步处理。

要查看 traceback 对象包含的内容,需要先引进 traceback 模块,然后调用 traceback 模块中的 print_tb() 方法,并将 sys.exc_info() 作为参数传入。

举例如下所示:

import sys  # 导入 sys 模块
import traceback  # 导入 traceback 模块


def demo1(a, b):
        c = a / b
        print("相除的商为:", c)


def demo2(a, b):
    demo1(a, b)


def demo3(a, b):
    demo2(a, b)


try:
    a = int(input("请输入被除数:"))
    b = int(input("请输入除数:"))
    demo3(a, b)
except:  # 未指定具体要捕获的异常类型,这种省略异常类的 except 语句也是合法的,它表示可捕获所有类型的异常,一般会作为异常捕获的最后一个 except 块。
    print(sys.exc_info())
    traceback.print_tb(sys.exc_info()[2])  # 进一步处理

运行结果:

请输入被除数:6
请输入除数:0
(<class 'ZeroDivisionError'>, ZeroDivisionError('division by zero'), <traceback object at 0x0000025DC895CB40>)
  File "D:/Data/ProfessionalSkills/Python/PycharmProjects/demo/demo.py", line 33, in <module>
    demo3(a, b)
  File "D:/Data/ProfessionalSkills/Python/PycharmProjects/demo/demo.py", line 27, in demo3
    demo2(a, b)
  File "D:/Data/ProfessionalSkills/Python/PycharmProjects/demo/demo.py", line 23, in demo2
    demo1(a, b)
  File "D:/Data/ProfessionalSkills/Python/PycharmProjects/demo/demo.py", line 18, in demo1
    c = a / b

(3)使用 Python traceback 模块获取异常信息

traceback模块可以用来查看异常的传播轨迹,追踪异常触发的源头。

应用程序运行时,经常会发生一系列函数或方法调用。异常的传播则相反,只要异常没有被完全捕获(包括异常没有被捕获,或者异常被处理后重新引发了新异常),异常就从发生异常的函数或方法逐渐向外传播,首先传给该函数或方法的调用者,该函数或方法的调用者再传给其调用者,直至最后传到 Python 解释器,此时 Python 解释器会中止该程序,并打印异常的传播轨迹信息。

建议:从最后一行异常信息开始看,最后一行信息详细显示了异常的类型和异常的详细消息。从这一行向上,逐个记录了异常发生源头、异常依次传播所经过的轨迹,并标明异常发生在哪个文件、哪一行、哪个函数处。

使用 traceback 模块查看异常传播轨迹,首先需要将 traceback 模块引入,该模块提供了如下两个常用方法:

  • print_exc([limit[, file]]):将异常传播轨迹信息输出到控制台或指定文件中。
    (1) limit:用于限制显示异常传播的层数,比如函数 A 调用函数 B,函数 B 发生了异常,如果指定 limit=1,则只显示函数 A 里面发生的异常。如果不设置 limit 参数,则默认全部显示。
    (2) file:指定将异常传播轨迹信息输出到指定文件中。如果不指定该参数,则默认输出到控制台。
  • format_exc():将异常传播轨迹信息转换成字符串。和 print_exc() 效果是一样的。

举例如下所示:

例一,print_exc()

import traceback  # 导入 traceback 模块


def demo1(a, b):
        c = a / b
        print("相除的商为:", c)


def demo2(a, b):
    demo1(a, b)


def demo3(a, b):
    demo2(a, b)


try:
    a = int(input("请输入被除数:"))
    b = int(input("请输入除数:"))
    demo3(a, b)
except:  # 未指定具体要捕获的异常类型,这种省略异常类的 except 语句也是合法的,它表示可捕获所有类型的异常,一般会作为异常捕获的最后一个 except 块。
    traceback.print_exc()

运行结果:

请输入被除数:6
请输入除数:0
Traceback (most recent call last):
  File "D:/Data/ProfessionalSkills/Python/PycharmProjects/demo/demo.py", line 30, in <module>
    demo3(a, b)
  File "D:/Data/ProfessionalSkills/Python/PycharmProjects/demo/demo.py", line 24, in demo3
    demo2(a, b)
  File "D:/Data/ProfessionalSkills/Python/PycharmProjects/demo/demo.py", line 20, in demo2
    demo1(a, b)
  File "D:/Data/ProfessionalSkills/Python/PycharmProjects/demo/demo.py", line 15, in demo1
    c = a / b
ZeroDivisionError: division by zero

例二,format_exc()

import traceback  # 导入 traceback 模块


def demo1(a, b):
        c = a / b
        print("相除的商为:", c)


def demo2(a, b):
    demo1(a, b)


def demo3(a, b):
    demo2(a, b)


try:
    a = int(input("请输入被除数:"))
    b = int(input("请输入除数:"))
    demo3(a, b)
except:  # 未指定具体要捕获的异常类型,这种省略异常类的 except 语句也是合法的,它表示可捕获所有类型的异常,一般会作为异常捕获的最后一个 except 块。
    print(traceback.format_exc())

运行结果:

请输入被除数:6
请输入除数:0
Traceback (most recent call last):
  File "D:/Data/ProfessionalSkills/Python/PycharmProjects/demo/demo.py", line 30, in <module>
    demo3(a, b)
  File "D:/Data/ProfessionalSkills/Python/PycharmProjects/demo/demo.py", line 24, in demo3
    demo2(a, b)
  File "D:/Data/ProfessionalSkills/Python/PycharmProjects/demo/demo.py", line 20, in demo2
    demo1(a, b)
  File "D:/Data/ProfessionalSkills/Python/PycharmProjects/demo/demo.py", line 15, in demo1
    c = a / b
ZeroDivisionError: division by zero

2 try except else 详解

使用 else 包裹的代码,只有当 try 块没有捕获到任何异常时,才会得到执行;
如果 try 块捕获到异常,即便调用对应的 except 处理完异常,else 块中的代码也不会得到执行。

举例如下所示:

例一,else 块执行

import traceback
try:
    res = 10 / int(input('请输入一个除数:'))
    print(res)
except:
    traceback.print_exc()
else:
    print('else 块执行。')

运行结果:

请输入一个除数:2
5.0
else 块执行。

例二,else 块不执行

import traceback
try:
    res = 10 / int(input('请输入一个除数:'))
    print(res)
except:
    traceback.print_exc()
else:
    print('else 块执行。')

运行结果:

请输入一个除数:0
Traceback (most recent call last):
  File "D:/Data/ProfessionalSkills/Python/PycharmProjects/demo/demo.py", line 3, in <module>
    res = 10 / int(input('请输入一个除数:'))
ZeroDivisionError: division by zero

3 try except finally 详解

Python 异常处理机制还提供了一个 finally 语句,通常用来为 try 块中的程序做扫尾清理工作。例如,当 try 块中的程序打开了一些物理资源(文件、数据库连接等)时,由于这些资源必须手动回收,而回收工作通常就放在 finally 块中,这是一种比较好的选择。Python 垃圾回收机制,只能帮我们回收变量、类对象占用的内存,而无法自动完成类似关闭文件、数据库连接等这些的工作。

无论 try 块是否发生异常,最终都要进入 finally 语句,并执行其中的代码块。

注意:finally 语句和 else 语句不同,finally 只要求和 try 搭配使用,而至于该结构中是否包含 except 以及 else,对于 finally 不是必须的(else 必须和 try except 搭配使用)。

举例如下所示:

例一,没触发异常

import traceback
try:
    res = 10 / int(input('请输入一个除数:'))
    print(res)
except:
    traceback.print_exc()
else:
    print('else 块执行。')
finally:
    print('finally 块执行。')

运行结果:

请输入一个除数:2
5.0
else 块执行。
finally 块执行。

例二,触发异常

import traceback
try:
    res = 10 / int(input('请输入一个除数:'))
    print(res)
except:
    traceback.print_exc()
else:
    print('else 块执行。')
finally:
    print('finally 块执行。')

运行结果:

请输入一个除数:0
finally 块执行。
Traceback (most recent call last):
  File "D:/Data/ProfessionalSkills/Python/PycharmProjects/demo/demo.py", line 3, in <module>
    res = 10 / int(input('请输入一个除数:'))
ZeroDivisionError: division by zero

【友情链接】

微信公众号:码农阿杰

博客园

【参考资料】

Python 官网

Python 3.8.2 documentation

猜你喜欢

转载自blog.csdn.net/manongajie/article/details/106341680