深入浅出Python异常处理 - 你所不知道的Python异常

在这里插入图片描述

深入浅出Python异常处理 - 你所不知道的Python异常

前言

Python编程开发中,异常处理扮演者至关重要的角色。合适的异常处理不仅可以提高代码的健壮性,还能增强程序的可读性和可维护性。在Python编程中,有效地管理异常是提高代码质量的关键一环。

本文旨在深入浅出地介绍Python中的异常处理,从基本概念到高级应用,我们将一步步探索Python异常处理的世界,揭示那些你可能未曾了解的知识和技巧。

异常处理概览

在``Python3.12.0的官方文档中,对异常的解释如下:
官方文档:https://docs.python.org/zh-cn/3/tutorial/errors.html

执行时检测到的错误称为 异常

什么是异常?

Python 中,异常指的是程序执行过程中发生的错误或意外情况。它们可能由各种原因产生,例如无效的输入、错误的操作或其他外部因素。当 Python 检测到这些情况时,会引发(或抛出)一个异常。异常本质上是一个对象,属于 Exception 类或其子类。代表了程序中出现的错误或意外。如果这个异常没有得到适当的处理,程序将终止并显示错误信息。

异常处理的重要性

异常处理是编写健壮且可维护代码的关键。适当的异常处理可以预防许多错误和程序崩溃,提高程序的稳定性和用户体验。通过有效的异常处理,我们可以控制程序在遇到错误时的反应,比如记录错误信息、恢复程序状态或通过警报通知用户。

异常处理的基本结构

Python 中的异常处理主要依赖于 try-except 语句块。将可能引发异常的代码放在 try 块中,并在 except 块中定义异常发生时的处理逻辑。此外,finallyelse 子句提供了更多的控制选项,允许在发生或未发生异常时执行特定代码。


标准异常类型

Python中的所有异常都继承自 BaseException 类。最常见的异常基类是 ExceptionPython标准库定义了许多不同类型的异常(如 ValueError, TypeError, IndexError, KeyError 等),它们都继承自 Exception

除此之外,Python也允许我们定义自己的异常类型,以适应特定的业务逻辑需求。

贴一个在菜鸟教程上的关于Python标准异常类型的表格:

异常名称描述
BaseException所有异常的基类
SystemExit解释器请求退出
KeyboardInterrupt用户中断执行(通常是输入^C)
Exception常规错误的基类
StopIteration迭代器没有更多的值
GeneratorExit生成器(generator)发生异常来通知退出
StandardError所有的内建标准异常的基类
ArithmeticError所有数值计算错误的基类
FloatingPointError浮点计算错误
OverflowError数值运算超出最大限制
ZeroDivisionError除(或取模)零 (所有数据类型)
AssertionError断言语句失败
AttributeError对象没有这个属性
EOFError没有内建输入,到达EOF 标记
EnvironmentError操作系统错误的基类
IOError输入/输出操作失败
OSError操作系统错误
WindowsError系统调用失败
ImportError导入模块/对象失败
LookupError无效数据查询的基类
IndexError序列中没有此索引(index)
KeyError映射中没有这个键
MemoryError内存溢出错误(对于Python 解释器不是致命的)
NameError未声明/初始化对象 (没有属性)
UnboundLocalError访问未初始化的本地变量
ReferenceError弱引用(Weak reference)试图访问已经垃圾回收了的对象
RuntimeError一般的运行时错误
NotImplementedError尚未实现的方法
SyntaxErrorPython 语法错误
IndentationError缩进错误
TabErrorTab 和空格混用
SystemError一般的解释器系统错误
TypeError对类型无效的操作
ValueError传入无效的参数
UnicodeErrorUnicode 相关的错误
UnicodeDecodeErrorUnicode 解码时的错误
UnicodeEncodeErrorUnicode 编码时错误
UnicodeTranslateErrorUnicode 转换时错误
Warning警告的基类
DeprecationWarning关于被弃用的特征的警告
FutureWarning关于构造将来语义会有改变的警告
OverflowWarning旧的关于自动提升为长整型(long)的警告
PendingDeprecationWarning关于特性将会被废弃的警告
RuntimeWarning可疑的运行时行为(runtime behavior)的警告
SyntaxWarning可疑的语法的警告
UserWarning用户代码生成的警告

异常基本信息 & 语法

Python提供了一套异常处理机制,允许程序在发生异常时优雅地处理。

下面的这些部分共同构成了Python中异常处理的基础,在对它们充分理解后,可以帮助我们写出更健壮和易于维护的代码哦。

基本语法

Python中,异常处理的基本结构包括 try, except, else, 和 finally 块。

基本结构

try:
    # 尝试执行的代码
except ExceptionType:
    # 当上述代码引发ExceptionType异常时执行的代码
else:
    # 如果没有异常发生执行的代码
finally:
    # 无论是否发生异常都会执行的代码
  • try 块中放置的是可能引发异常的代码。
  • except 块定义了当特定异常发生时的处理逻辑。
  • else 块中的代码仅在没有异常发生时执行。
  • finally 块中的代码无论是否发生异常都会执行,通常用于执行一些清理工作。

示例

try:
    result = 10 / 2
except ZeroDivisionError:
    print("发生了除以零的错误")
else:
    print("结果是", result)
finally:
    print("执行完毕,无论是否发生异常")

在这个示例中,如果 try 块中的代码成功执行(即没有发生异常),那么 else 块中的代码将被执行。无论是否发生异常,finally 块中的代码都会被执行,通常用于资源释放或清理工作。

通过这种结构,Python的异常处理不仅可以处理错误情况,还能在正常情况下执行特定代码,并且确保最终总能执行某些必要的清理操作。


引发异常 (raise)

我们可以使用 raise 语句手动引发异常。这通常在检测到某些条件下,继续执行程序会导致问题时使用。

def check_age(age):
    if age < 18:
        raise ValueError("年龄不足18岁")


捕获异常 (try-except)

使用 try-except 语句块可以捕获和处理异常,防止程序意外崩溃。

try:
    check_age(15)
except ValueError as error:
    print("捕获到异常:", error)


获取详细异常信息(sys.exc_info)

在处理异常时,有时仅知道发生了异常并不足够,我们可能还需要了解异常发生时的更多上下文信息。Python提供了 sys.exc_info() 函数,它可以在异常处理块内被调用,以获取正在处理的异常的详细信息。

当在一个 except 块中调用 sys.exc_info() 时,它会返回一个包含三个值的元组,分别是:

  1. 异常类型 (exc_type): 引发的异常的类型。
  2. 异常值 (exc_value): 异常实例本身,通常包含错误消息。
  3. 追踪回溯对象 (exc_traceback): 包含异常发生时调用堆栈的详细信息的对象。

这些信息对于调试问题和记录错误日志非常有用。

示例

  • 通过使用 sys.exc_info(),可以获取到有关当前异常的更详尽的信息。
import sys
import traceback

try:
    # 可能引发异常的代码
    1/ 0
except ZeroDivisionError:
    exc_type, exc_value, exc_traceback = sys.exc_info()
    print("异常类型:", exc_type)
    print("异常值:", exc_value)
    print("追踪回溯:")
    # 直接打印异常的追踪信息到标准输出
    traceback.print_tb(exc_traceback)

代码运行结果如下:

在这里插入图片描述


基础异常处理代码

在实际应用中,通常会将引发和捕获异常结合起来,以确保程序的健壮性和可维护性。

示例

import sys
import traceback

def check_age(age):
    if age < 18:
        raise ValueError("年龄不足18岁")

try:
    age = 15
    check_age(age)
except ValueError as error:
    exc_type, exc_value, exc_traceback = sys.exc_info()
    print("捕获到异常:", error)
    print("异常类型:", exc_type)
    print("异常信息:", exc_value)
    print("追踪回溯:")
    # 获取异常的追踪信息。返回的是追踪信息的字符串列表,每个字符串代表追踪信息中的一行
    print(''.join(traceback.format_tb(exc_traceback)))
finally:
    print("处理完毕")

在这个示例中,当 check_age 函数引发 ValueError 异常时,except 块不仅会打印出异常信息,还会通过 sys.exc_info() 获取并打印出异常的类型、具体信息以及追踪回溯。这样做可以帮助我们更准确地理解异常发生的上下文,对于调试和记录错误日志非常有用。

代码运行效果如下:

在这里插入图片描述


自定义异常 (Custom Exceptions)

应用场景

  • 特定业务逻辑:当标准异常无法准确描述特定业务逻辑错误时。

优势

  • 代码清晰度:使错误处理更直观,提高代码的可读性。
  • 错误分类:方便对特定类型的错误进行特殊处理。

基础示例:

class MyCustomError(Exception):
    """一个自定义的异常类,用于特定的错误情况。"""


def my_function(value):
    if not value:
        raise MyCustomError("值不能为空")


再来一个接近真实编程环境中的示例代码,

示例代码

class NegativeValueError(Exception):
    """当输入值为负数时引发的异常。"""

    def __init__(self, value):
        self.value = value
        self.message = "输入值不应为负数"
        super().__init__(self.message)


def check_positive(number):
    if number < 0:
        raise NegativeValueError(number)
    return number


try:
    result = check_positive(-10)
except NegativeValueError as e:
    print(f"错误:{e.message} - 输入值: {e.value}")

代码释义:

这段代码定义了一个名为 NegativeValueError 的自定义异常类,用于处理输入值为负数的错误情况:

  1. 自定义异常类 NegativeValueError
    • 继承自Python的基本Exception类。
    • 包含一个构造函数,接受一个value参数,并设置一个错误消息"输入值不应为负数"
    • 通过super().__init__(self.message)调用基类构造函数,将错误消息传递给基类。
  2. 函数 check_positive
    • 这个函数接受一个数字作为参数。
    • 如果数字小于零,函数将引发NegativeValueError异常,传递该数字作为参数。
    • 如果数字非负,函数正常返回该数字。
  3. 异常处理
    • 使用try-except块来捕获NegativeValueError异常。
    • 如果捕获到此异常,将打印出错误消息和导致异常的值。

代码运行效果:

在这里插入图片描述


异常链 (Exception Chaining)

应用场景

  • 复杂的错误处理:在一个复杂的程序中,处理异常时可能会引发另一个异常,异常链可以帮助追踪这一过程。
  • 调试和日志记录:当需要详细记录或调试一个异常的整个发生过程时,异常链提供了一种追踪原始异常源的方法。

优势

  • 保留异常上下文:允许开发人员看到异常的“历史”,从最初的异常到最终的异常。
  • 提高可调试性:通过保留异常链,开发者可以更容易地理解错误的根本原因,特别是在复杂的系统中。

基础语法:

  • 异常链是通过在raise语句中使用from关键字来实现的。
  • SomeException是尝试捕获的异常类型,NewException是想要引发的新异常类型。通过使用from e,将原始的异常信息附加到新的异常上,从而创建了一个异常链。
try:
    # 尝试执行某些可能引发异常的操作
except SomeException as e:
    # 处理这个异常
    raise NewException("An error occurred") from e


先来看官方文档的示例:

def func():
    raise ConnectionError


try:
    func()
except ConnectionError as exc:
    raise RuntimeError('Failed to open database') from exc

代码运行效果如下:

  • 上述异常是导致以下异常的直接原因(直接就明说了,第2行是导致第8行错误的直接原因)

在这里插入图片描述


进一步的,我使用一个真实的例子,以方便大家的理解。

import json
import sys
import traceback


class DatabaseConnectionError(Exception):
    """自定义数据库连接异常"""

    def __init__(self, message):
        super().__init__(message)


def save_to_database(data):
    # 假设的数据库保存操作
    # 这里我们故意抛出一个异常来模拟数据库连接错误
    raise DatabaseConnectionError("数据库连接失败")


def process_file(filename):
    try:
        with open(filename, 'r') as file:
            data = json.load(file)
            save_to_database(data)
    except FileNotFoundError as e:
        raise RuntimeError("文件未找到") from e
    except DatabaseConnectionError as e:
        raise RuntimeError("数据库操作失败") from e


if __name__ == '__main__':
    # 调用函数
    try:
        process_file("data.json")
    except RuntimeError as e:
        print(f"捕获到异常: {e}")
        print(f"原始异常: {e.__cause__}")
        exc_type, exc_value, exc_traceback = sys.exc_info()
        traceback_details = ''.join(traceback.format_exception(exc_type, exc_value, exc_traceback))
        print("\nTraceback details:\n", traceback_details)

代码释义:

  1. 自定义异常类:定义 DatabaseConnectionError,创建了一个异常类型,用于数据库连接错误。

  2. 异常封装:在 process_file 函数中,捕获了两种类型的异常(FileNotFoundErrorDatabaseConnectionError),并将它们封装成 RuntimeError,同时保留了原始异常的信息。这是通过 from e 语法实现的,它创建了一个异常链,保留了底层异常的上下文。

  3. 异常信息打印:在外层的 try-except 块中,不仅打印了捕获的 RuntimeError 的信息,还打印了原始异常(e.__cause__)的信息,这有助于理解异常发生的完整路径。

  4. 完整的调用栈信息:通过使用 sys.exc_info()traceback.format_exception,能够获取并打印出异常发生时的完整调用栈信息。这对于定位异常发生的具体位置非常有用。

代码运行效果如下:

  • 异常信息打印的非常清晰~
    在这里插入图片描述

最佳实践和常见错误

最佳实践

  1. 使用具体的异常:捕获特定异常,避免通用异常捕获,以提供更准确的错误处理和反馈。
  2. 避免空的异常处理:不留空的except块,即使是简单记录错误也好。
  3. 使用异常链:在处理异常时保留原始异常信息,有助于调试和理解错误源。
  4. 资源清理:使用finally块或with语句确保资源(如文件和网络连接)得到适当清理。
  5. 自定义异常:当标准异常不足以表达特定错误时,定义自定义异常,使代码更易理解和维护。
  6. 异常消息的清晰度:提供清晰、具体的异常消息,有助于快速定位问题。
  7. 避免过多的异常处理:避免不必要或过度的异常捕获,以防隐藏代码中的真正问题。

常见错误

  1. 捕获太宽泛的异常:避免使用过于广泛的异常捕获(如except Exception:),可能会掩盖其他错误。
  2. 重复的异常处理:避免在多层函数调用中重复处理相同的异常,以免导致代码冗余和混乱。
  3. 忽视异常:不使用空的except块或完全忽视异常,以免错过潜在的错误。
  4. 异常滥用:避免使用异常来控制正常的程序流程,这不是异常设计的初衷。

总结✨✨

本文全面地介绍了Python中异常处理的各个方面,从基础知识到高级应用,提供了深入理解和有效管理异常的方法。

Python的异常处理是一个强大且灵活的机制,允许我们以优雅和有效的方式处理错误和意外情况。

重要的是要理解异常处理的原则和目的,即在不正常或意外的情况下保护程序的稳定性和可预测性。通过遵循最佳实践并避免常见的错误,我们可以编写更健壮、更可读且易于维护的代码。

后话

本次分享到此结束,

see you~🐱‍🏍🐱‍🏍

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

使用c++解压rar文件,基于UnRAR64,非命令行

最近项目需要解压缩rar文件&#xff0c;我们都知道rar是闭源收费软件&#xff0c;如果直接采用命令行可能会有限制&#xff0c;或者盗版问题&#xff0c;使用正版的winrar命令行解压rar文件是否有限制&#xff0c;这个我没来得及测试&#xff0c;但是从交互体验上来说&#xff…

EFCore: The ConnectionString property has not been initialized.

使用NuGet的程序包管理控制台执行命令“update-database”的时候报出该错误 经过检查发现是optionsBuilder.UseSqlServer(strConn);中的strConn没有写

【python海洋专题四十六】研究区域示意放大图

【python海洋专题四十六】研究区域示意放大图 图片 往期推荐 图片 【python海洋专题一】查看数据nc文件的属性并输出属性到txt文件 【python海洋专题二】读取水深nc文件并水深地形图 【python海洋专题三】图像修饰之画布和坐标轴 【Python海洋专题四】之水深地图图像修饰 …

穿越内存迷宫:C语言地址与指针的的冒险之旅

前言 C语言的魅力在于其直接的内存控制&#xff0c;而地址和指针是这种控制的核心。深入了解这些概念&#xff0c;将帮助我们更好地理解和利用C语言的潜力。本文将带领你踏上地址与指针的奇妙之旅&#xff0c;揭示它们在程序设计中的神秘面纱。 奇妙指针之旅&#xff1a;解码…

日语形容词分类

かっこいい的否定变形是かっこよくない

Selenium自动化测试细节讲解

与以前瀑布式开发模式不同&#xff0c;现在软件测试人员具有使用自动化工具执行测试用例套件的优势&#xff0c;而以前&#xff0c;测试人员习惯于通过测试脚本执行来完成测试。 但自动化测试的目的不是完全摆脱手动测试&#xff0c;而是最大程度地减少手动运行的测试。自动化…

计算机毕业设计选题推荐-农产品销售微信小程序/安卓APP-项目实战

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

抖音小程序开发:打造高效餐饮团购平台的技术指南

在餐饮行业&#xff0c;通过抖音小程序开发一个高效的团购平台&#xff0c;可以为餐厅提供更广泛的曝光&#xff0c;增加销售机会。本文将从技术角度出发&#xff0c;为您提供一份详细的抖音小程序开发指南&#xff0c;助您打造一流的餐饮团购平台。 一、确定需求和功能 在开…

让各大运营商都默默流泪的 HTTPS 协议(HTTPS 的加密流程)

文章目录 前言1. 什么是 HTTPS1.1 臭名昭著的 "运营商劫持" 2. 什么是"加密"3. HTTPS 的加密流程3.1 对称加密用对称加密可行吗&#xff1f; 3.2 引入非对称加密用对称加密非对称加密可行吗&#xff1f; 3.3 中间人攻击如何证明浏览器收到的公钥一定是该网…

栈 和 队列

什么是栈? 一种特殊的线性表&#xff0c;只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶&#xff0c;另一端称为栈底。栈中的数据元素遵守后进先出&#xff08;LIFO - Last In First Out&#xff09;的原则。   从数据结构的角度来看&…

【图文详解】Android Studio(新版本) 配置OpenCV库,解决出现的各种问题

前言 写这篇文章的目的就是记录自己在配置OpenCV库时遇到的问题。在网上查找相关资料时&#xff0c;发现很多Android Studio都是老版本&#xff0c;并且出现的问题都不能被解决。自己在配置过程中出现的问题都进行记录下来并一一解决。 新建项目 点击 New Project 选择界面 …

STM32F4X SDIO(九) 例程讲解-SD卡擦除、读写

STM32F4X SDIO &#xff08;九&#xff09; 例程讲解-SD卡擦除、读写 例程讲解-SD卡擦除、读写SD卡擦除CMD32:ERASE_WR_BLK_START命令发送命令响应 CMD33:ERASE_WR_BLK_END命令发送命令响应CMD38:ERASE命令响应 CMD13:SD_CMD_SEND_STATUS命令发送命令回应 SD卡读数据CMD16:SET_…

【LeetCode】挑战100天 Day10(热题+面试经典150题)

【LeetCode】挑战100天 Day10&#xff08;热题面试经典150题&#xff09; 一、LeetCode介绍二、LeetCode 热题 HOT 100-122.1 题目2.2 题解 三、面试经典 150 题-123.1 题目3.2 题解 一、LeetCode介绍 LeetCode是一个在线编程网站&#xff0c;提供各种算法和数据结构的题目&…

matplotlib 设置标签和图例

常用标签 xlabel&#xff1a;x轴标签名称。 ylabel&#xff1a;y轴标签名称。 title&#xff1a;图像标题。 设置x和y轴的刻度&#xff1a;xticks和yticks。 nums np.arange(0, 1.3, 0.01)# 设置标题 plt.title("title") # 设置横坐标信息 plt.xlabel("x-…

复杂度计算实例

1.常见时间复杂度计算举例 实例1 实例1基本操作执行了2N10次&#xff0c;通过推导大O阶方法知道&#xff0c;时间复杂度为 O(N) 实例2 实例2基本操作执行了MN次&#xff0c;有两个未知数M和N&#xff0c;时间复杂度为 O(NM) 实例3 实例3基本操作执行了100次&#xff0c;通过…

C++学习笔记(二):C++是如何运行的

C是如何运行的 include 预处理语句&#xff0c;在编译前就会被处理。 main函数 程序入口。 #include <iostream>int main() {std::cout << "Hello World!" << std::endl;std::cin.get();return 0; }Visual Studio 解决方案平台指的是编译的代码的…

探索微信小程序框架的精华——高质量的优秀选择

目录 引言&#xff1a; 1. 框架性能 2. 开发者工具支持 3. 文档和社区支持 4. 扩展能力 5. 使用率和稳定性 结语&#xff1a; 引言&#xff1a; 微信小程序作为一种轻量级、高效便捷的应用形式&#xff0c;已经在移动应用领域占据了重要地位。而其中&#xff0c;选择一个…

Nussbaumer Transform 以及 Amortized FHEW bootstrapping

参考文献&#xff1a; [Nuss80] Nussbaumer H. Fast polynomial transform methods for multidimensional DFTs[C]//ICASSP’80. IEEE International Conference on Acoustics, Speech, and Signal Processing. IEEE, 1980, 5: 235-237.[SV11] Smart N P, Vercauteren F. Full…

C++ 配合图形库实现画线效果

#include<stdio.h> #include <conio.h> #include<math.h> #include <graphics.h> // 引用图形库头文件 #define N 12 int List[N][N];void draw() {for (int i 0; i < N; i) {int x 200 * cos(2 * 3.14 * i / N);int y 200 * sin(2 * 3.1…

归并排序 merge Sort + 图解 + 递归 / 非递归

归并排序(merge sort)的主要思想是&#xff1a;将若干个有序序列逐步归并&#xff0c;最终归并为一个有序序列二路归并排序(2-way merge sort)是归并排序中最简单的排序方法 &#xff08;1&#xff09;二路归并排序的递归实现 // 二路归并排序的递归实现 void merge(vector&l…