09. 异常处理

目录

1、前言

2、常见的异常

3、异常处理try...except...finally

4、异常信息解读

5、raise

6、自定义异常

7、小结


1、前言

在编程中,异常(Exception)是程序在运行期间检测到的错误或异常状况。当程序执行过程中发生了一些无法继续执行的错误时,会引发异常,这可能是由于错误的输入、文件不存在、网络连接问题等多种原因引起的。而程序中对于异常的处理,是为了保持良好的程序健壮性,不会因为异常而导致程序终止甚至退出。

2、常见的异常

在Python中,异常是一个类的实例,通常是内置的异常类的子类。当某个异常条件触发时,Python会抛出(raise)一个异常对象,然后程序的控制流将被转移到处理该异常的代码块。异常处理的机制允许程序员在程序中检测并处理错误,以避免程序崩溃。

一般来说,异常包含了3大部分:异常类型、异常信息、异常堆栈。

1)异常类型:异常类型是指异常的分类,它指定了异常的种类。指示了引发异常的具体情况。

try:result = 10 / 0  # 引发 ZeroDivisionError
except ZeroDivisionError as e:# 异常类型是 ZeroDivisionErrorprint(f"Error Type: {type(e).__name__}")

2)异常信息:是一条包含有关异常原因的人类可读的描述。异常消息通常包含错误的详细信息,有助于开发者理解异常的具体原因。

try:result = int("abc")  # 引发 ValueError
except ValueError as e:# 异常消息是 "invalid literal for int() with base 10: 'abc'"print(f"Error Message: {str(e)}")

3)异常堆栈:异常堆栈信息包含了异常发生时程序调用栈的状态,它记录了异常的触发点以及导致异常的函数调用链。异常堆栈信息对于调试和定位问题非常有用。

def func1():result = int("abc")  # 引发 ValueErrordef func2():func1()try:func2()
except ValueError as e:# 异常堆栈信息包含了函数调用链print(f"Exception Traceback: {e.with_traceback(e.__traceback__)}")

而在Python中常见的异常类有:

  • ZeroDivisionError:除以零错误
  • ValueError:传入一个调用者不期望的值,即使值的类型是正确的
  • TypeError:操作或函数的参数类型错误
  • FileNotFoundError:文件不存在错误
  • IndexError:索引超出序列范围
  • KeyError:字典中的键不存在
  • NameError:尝试访问未声明的变量

3、异常处理try...except...finally

传统来讲,如果程序在运行过程中发生了异常,可以实现约定好一些错误码,利用错误码来区分各种异常事件,典型的诸如Http状态码。这样根据不同的错误码就可以很清楚的知道是什么错误类型。但是如果错误码很多,那么维护起来就很不方便,而且错误码通常需要和业务代码结合在一起:

def method1():code = do_something()if code == 200:return "ok"elif code == 404:return "resource not found"elif code == 500:return "server internal error"......else:return "other error"def do_something():return 100

因此,Python内置了一套异常处理机制。在 Python 3 中,异常处理是通过使用 try, except, else, 和 finally 等关键字来实现的。异常处理的目的是在程序执行期间检测到错误,并提供一种机制来处理这些错误,防止程序中断或崩溃。以下是异常处理的基本语法:

try:# 可能引发异常的代码块result = 10 / 0  # 这里故意引发一个除零错误
except ZeroDivisionError as e:# 异常处理块print(f"Error: {e}")
else:# 如果没有发生异常时执行的代码块print("No exception occurred.")
finally:# 无论是否发生异常,都会执行的代码块print("Finally block executed.")

try 语句包裹了可能引发异常的代码块。如果在 try 语句中发生异常,程序将跳转到匹配的 except 语句块,执行相应的异常处理逻辑。如果没有异常发生,那么会执行 else 语句块中的代码。最后,无论是否发生异常,都会执行 finally 语句块中的代码。

上述代码执行后的结果:

当然这里的except捕获的异常可以有不同类型,如:

def test():try:# 可能引发异常的代码块result = 10 / 0  # 这里故意引发一个除零错误,会抛出ZeroDivisionErrorresult = 10 / int('a')  # 这里故意引发一个字符串转换类型错误,会抛出ValueError"hello" + b   # 这里故意引用一个未被声明的变量,会抛出NameErrorexcept ValueError as ve:  # 当发生ValueError时候,被这里的异常捕获# 异常处理块print(f"raise a exception : ValueError: {ve}")except ZeroDivisionError as ze:  # 当发生ZeroDivisionError时候,被这里的异常捕获# 异常处理块print(f"raise a exception : ZeroDivisionError: {ze}")except Exception as e:     # 当发生的异常上面都没有捕获时,最终会被这层捕获# 异常处理块print(f"raise a exception : Exception: {e}")else:# 如果没有发生异常时执行的代码块print("No exception occurred.")finally:# 无论是否发生异常,都会执行的代码块print("Finally block executed.")test()

需要注意的是,这里的异常是逐层捕获的,越靠经try的except优先级越高。如果第一层except就捕获了Exception,那么接下来的ValueError都是捕获不到的。

def test():try:# 可能引发异常的代码块result = 10 / 0  # 这里故意引发一个除零错误,会抛出ZeroDivisionError# result = 10 / int('a')  # 这里故意引发一个字符串转换类型错误,会抛出ValueError"hello" + b   # 这里故意引用一个未被声明的变量,会抛出NameErrorexcept Exception as ve:  # 调换一下顺序,把Exception放在第一层# 异常处理块print(f"raise a exception : Exception: {ve}")except ZeroDivisionError as ze:  # 当发生ZeroDivisionError时候,被这里的异常捕获# 异常处理块print(f"raise a exception : ZeroDivisionError: {ze}")except ValueError as e:     # 当发生的异常上面都没有捕获时,最终会被这层捕获# 异常处理块print(f"raise a exception : ValueError: {e}")else:# 如果没有发生异常时执行的代码块print("No exception occurred.")finally:# 无论是否发生异常,都会执行的代码块print("Finally block executed.")test()

照理说,10/0会抛出ZeroDivisionError异常类型,但是由于Exception类型比ZeroDivisionError更靠近try,所以优先被Exception捕获。

因为Exception是所有异常类的基类。ValueError或NameError等异常都继承于Exception,因此Exception可以捕获所有属于它自己的子类异常类型。如果不存在继承关系,那么优先级属于平级,就会按照异常类型各自捕获。因此项目中,我们往往会把Exception最为保底的异常捕获类型来处理。

我们点开ValueError源码可以看到继承关系:

此外,使用try...except还有一个好处是,它可以跨层调用。如:

def test():try:result = test1()  # 这里调用test1方法,test1方法会抛出异常,由上层捕获except Exception as ve: # 异常处理块print(f"raise a exception : Exception: {ve}")finally:# 无论是否发生异常,都会执行的代码块print("Finally block executed.")def test1():return 1 / 0test()

这样,我们就不需要在每个调用方法的地方都进行异常捕获,只要在合适的层(如在统一入口进行捕获)就可以捕获到各个层次间的异常信息。而如果异常没有被捕获,则会一直网上抛,直到被Python解释器捕获,然后程序退出。

4、异常信息解读

上面我们介绍了基本的异常处理的语法。既然出现了异常,那么我们肯定是要进行修复的。那么读懂异常信息就很关键。前面介绍到异常一般分为3个部分,异常类型和异常信息就不说了,通常都很容易看懂。主要我们来看下异常堆栈,这里包含了异常的整个方法调用链,从中我们可以很容易看到具体哪个方法出现了异常。我们先来编写一段代码,模拟下异常:

def do_something():return 1 / 0def test():try:result = do_something()  # 这里调用do_something方法,do_something方法会抛出异常,由上层捕获except Exception as e:# 异常处理块,使用with_traceback()打印出异常堆栈信息print(f"raise a exception : Exception: {e.with_traceback()}")finally:# 无论是否发生异常,都会执行的代码块print("Finally block executed.")test()

执行结果:

所以可以看到,通过跟踪异常的堆栈信息,可以很容易定位到具体的错误代码。

注:使用e.with_traceback()打印的错误信息,只能在控制终端打印信息,并不能持久化。一般项目中需要把错误信息记录的日志文件中,方便排查。可以引入logging模块,使用logging记录到日志中

5、raise

除了try...except被动的捕获程序异常以外,我们还可以手动的进行抛出已识别的异常信息。这时就要用到raise关键字。通过 raise 关键字,你可以显式地引发异常,并指定异常类型、异常消息等信息。这对于在特定条件下主动引发异常、或在异常发生时进行额外的信息记录非常有用。

基础语法很简单:

raise 异常类("异常信息")

如:

def example_function(value):if value < 0:raise ValueError("这里引发一个异常,value值不能<0")return valuetry:result = example_function(-5)
except ValueError as e:print(f"捕获到异常: {e}")

example_function 函数中使用 raise 关键字在 value 小于 0 时引发了 ValueError 异常,并提供了异常消息。在异常处理块中,程序捕获了这个异常并进行了处理。raise 语句可以包含一个异常类、一个异常类的实例,或者是一个异常类和一个异常消息:

# 引发指定类型的异常
raise ValueError("This is a custom error message")# 引发异常实例
custom_exception = ValueError("This is another custom error message")
raise custom_exception

使用 raise 关键字时需要注意,在没有捕获异常的情况下,异常会传递到调用栈的上层,直到被捕获或导致程序终止。因此,要慎重使用 raise,确保异常能够得到适当处理。

6、自定义异常

通常,结合raise使用的需要我们自定义异常类。根据不同的业务场景,定义符合业务场景类型的异常类。编写自定义异常时,需要继承异常的基类(Exception)或其子类,并在构造函数中设置一些自定义属性。如:

def example_function(value):if value < 0:raise CustomError(-500, "这里引发一个异常,value值不能<0")return value# 自定义异常CustomError,继承Exception
class CustomError(Exception):# 构造函数,需要提供异常代码,异常信息属性def __init__(self, code, message):self.code = codeself.message = messagesuper().__init__(message)try:result = example_function(-5)
except ValueError as e:print(f"捕获到异常: {e}")

CustomError 类继承自 Exception,并在其构造函数中定义了两个属性 code 和 message。在 example_function 中,当输入值小于 0 时,引发了自定义的异常,并在异常处理块中捕获并输出了异常的属性信息。

自定义异常的主要目的是提供更多的上下文信息,以便在异常发生时更好地理解问题的原因。在实际的应用中,可以根据具体的需求定义不同的自定义异常类,以便更好地组织和处理异常情况。

7、小结

总体来说,异常处理是一种良好的编程实践,有助于确保程序在面对各种异常情况时能够保持可控和可维护。通过适当的异常处理,开发者能够更好地应对意外情况,提高程序的质量和稳定性。

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

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

相关文章

C languange DGEQRF 示例,link liblapack.a

1.示例源码 #include <stdio.h>int min(int m, int n){ return m<n? m:n;}void print_matrix(double* A, int m, int n, int lda) {for (int i 0; i < m; i){for (int j 0; j < n; j){//printf("%7.4f ", A[i j*lda]);printf("%7.4f, &quo…

springboot 文件上传

前言 用户访问当前系统,将自己本地计算机中文件通过浏览器上传到当前系统所在的服务器过程中称之为文件的上传 具体思路 1.文件上传 文件上传: 用户将本地计算机中文件上传到服务器过程称之为文件上传 2.文件上传开发思路: a.提供一张上传页面 提交方式必须:post enctype属…

Oracle RMAN全备脚本(正式测试可行)

Oracle RMAN全备脚本 正式环境测试可行 请参考。 run{ allocate channel c1 type disk maxpiecesize20G; allocate channel c2 type disk maxpiecesize20G; allocate channel c3 type disk maxpiecesize20G; allocate channel c4 type disk maxpiecesize20G; crosscheck arch…

创建表与删除表(六)

表的基本操作&#xff08;六&#xff09; 一、创建表 1.1 使用DDL语句创建表 CREATE TABLE 表名(列名 类型,列名 类型......); 示例&#xff1a; 创建一个 employees 表包含雇员 ID &#xff0c;雇员名字&#xff0c;雇员薪水。 create table employees(employee_id int,em…

【python基础】—函数式编程三个基本函数map()/filter()/reduce()

文章目录 前言一、map()函数二、filter()函数三、reduce()函数 前言 map()、filter()、reduce()是python内置函数&#xff0c;它们提供了一种便捷的方式来处理可迭代对象中的元素&#xff0c;这些函数在许多编程任务中非常有用&#xff0c;包括数据转换&#xff0c;筛选和累积…

​LeetCode解法汇总2670. 找出不同元素数目差数组

目录链接&#xff1a; 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目&#xff1a; https://github.com/September26/java-algorithms 原题链接&#xff1a;力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 描述&#xff1a; 给你一个下…

从创新者到引领者:探索第四范式的AI之旅

大数据产业创新服务媒体 ——聚焦数据 改变商业 如今&#xff0c;人工智能已成为改变世界、驱动各行各业变革的核心源动力。在国内&#xff0c;有一些公司已走在前列&#xff0c;其中就包括北京第四范式智能技术股份有限公司&#xff0c;在AI这个赛道&#xff0c;他是一名创新…

Patch2QL:开源供应链漏洞挖掘和检测的新方向

背景 开源生态的上下游中&#xff0c;漏洞可能存在多种成因有渊源的其它缺陷&#xff0c;统称为“同源漏洞”&#xff0c;典型如&#xff1a; 上游代码复用缺陷。开源贡献者在实现功能相似的模块时&#xff0c;常复用已有模块代码或逻辑&#xff1b;当其中某个模块发现漏洞后…

聊聊 FTP、SFTP、FTPS

文章目录 FTP概述两种工作方式FTP、SFTP、FTPSSFTP和FTPS的区别命令详解SFTPFTP 个人简介 FTP概述 文件传送协议FTP(File Transfer Protocol)是TCP/IP协议簇中的一个成员&#xff0c;也是现在因特网上最广泛的文件传送协议。FTP协议包括两个部分&#xff0c;一个是FTP客户端&a…

java:java反编译工具--jd-gui

JD-GUI是一款反编译软件&#xff0c;JD分为JD-GUI、JD-Eclipse两种运行方式&#xff0c;JD-GUI是以单独的程序的方式运行&#xff0c;JD-Eclipse则是以一个Eclipse插件的方式运行。 官方下载地址&#xff1a; https://github.com/java-decompiler/jd-gui/releases 我这边下载…

Unity Shader 滚动进度条效果

Unity Shader 滚动进度条效果 前言项目场景布置导入图片修改场景设置修改图片尺寸即可调整进度 ASE连线 前言 UI要实现一个滚动进度&#xff0c;于是使用Shader制作一个。 项目 场景布置 导入图片 修改一下导入图片的格式&#xff0c;这样才能循环起来 WrapMode改为Repea…

提高 Code Review 质量的最佳实践

Code Review 是软件开发中至关重要的环节&#xff0c;它有助于确保代码质量、提高团队协作水平&#xff0c;同时也是一个学习和知识分享的机会。以下是一些提高 Code Review 质量的最佳实践&#xff1a; 1. 设置清晰的标准和目标 在进行 Code Review 之前&#xff0c;确保整个…

正点原子--STM32中断系统学习笔记(1)

1、什么是中断&#xff1f; 原子哥给出的概念是这样的&#xff1a;打断CPU正常执行的程序&#xff0c;转而处理紧急程序&#xff0c;然后返回原暂停的程序继续运行&#xff0c;就叫中断。 当发生中断时&#xff0c;当前执行的程序会被暂时中止&#xff0c;进而进入中断处理函…

Qt案例 使用WINDOWS API的VDS.H库查询/修改 WINDOWS系统中硬盘分区/盘符信息(二)

简单介绍使用vds.h中的类和方法操作修改硬件/盘符的一些常使用的结构和函数&#xff0c;包括获取格式、删除、创建分区&#xff0c;设置磁盘文件类型&#xff0c;格式化卷等&#xff1b; 值得注意的是即使在vds.h库中像格式化卷这种都有多个方法&#xff0c;需要根据实际需求选…

Node: opensslErrorStack: [ ‘error:03000086:digital envelope routines::initialization error‘ ]异常处理

目录 一、问题描述二、问题分析三、解决方案方案一&#xff1a;你可以按照以下步骤来删除 NODE_OPTIONS 环境变量中的 --openssl-legacy-provider 选项&#xff1a;方案二&#xff1a;在package.json更改scripts方案三&#xff1a;降级 Node.js 版本 在进行前端项目开发时&…

Linux+服务器后台运行程序

在Linux服务器直接运行程序&#xff0c;程序运行的时间较长&#xff0c;程序经常会因为网络连接问题异常终止&#xff0c;一直盯着程序运行又费时费力&#xff0c;这时后台运行程序是更好的解决方式。But&#xff0c;如果服务器重启了&#xff0c;那所有进程都断掉了&#xff0…

关于v8垃圾回收机制以及与其相关联的知识点--还没整理版本

对于值类型b来说&#xff0c;就直接释放了其占用的内存&#xff0c;对于引用类型obj来说&#xff0c;销毁的只是变量obj对堆内存地址 1001 的引用&#xff0c;obj的值 { c: 3 } 依然存在于堆内存中。那么堆内存中的变量如何进行回收呢&#xff1f; V8的垃圾回收策略主要是基于…

2024年美国大学生数学建模竞赛思路与源代码【2024美赛D题】

B站账号&#xff0c;提前关注&#xff0c;会有直播&#xff1a;有为社的个人空间-有为社个人主页-哔哩哔哩视频 (bilibili.com) 题目 待定 问题一 思路 待定 模型 待定 程序 待定 问题二 待定 思路 待定 模型 待定 程序 待定

【Java 数据结构】栈和队列

栈和队列 1. 栈(Stack)1.1 概念1.2 栈的使用1.3 栈的模拟实现1.4 栈的应用场景1.5 概念区分 2. 队列(Queue)2.1 概念2.2 队列的使用2.3 队列模拟实现2.4 循环队列 3. 双端队列 (Deque)4. 面试题 1. 栈(Stack) 1.1 概念 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在…

自然语言处理,基于预训练语言模型的方法,车万翔,引言部分

文章目录 自然语言处理应用任务1. 信息抽取2. 情感分析3. 问答系统4. 机器翻译5. 对话系统 自然语言处理应用任务 1. 信息抽取 信息抽取&#xff08;Information Extraction, IE&#xff09;&#xff0c;是从非结构化的文本中&#xff0c;抽取出结构化信息的过程&#xff0c;…