09. 异常处理

news2024/11/28 8:37:32

目录

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:
    # 异常类型是 ZeroDivisionError
    print(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")  # 引发 ValueError

def 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  # 这里故意引发一个除零错误,会抛出ZeroDivisionError
        result = 10 / int('a')  # 这里故意引发一个字符串转换类型错误,会抛出ValueError
        "hello" + b   # 这里故意引用一个未被声明的变量,会抛出NameError
    except 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   # 这里故意引用一个未被声明的变量,会抛出NameError
    except 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 / 0


test()

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

4、异常信息解读

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

def do_something():
    return 1 / 0


def 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 value


try:
    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 = code
        self.message = message
        super().__init__(message)


try:
    result = example_function(-5)
except ValueError as e:
    print(f"捕获到异常: {e}")

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

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

7、小结

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

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

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

相关文章

Monday.com替代工具大盘点:哪个更适合您的团队协作需求?

市场上有许多项目管理软件解决方案&#xff0c;每个都有自己的优点和缺点&#xff0c;根据您的具体需求和要求&#xff0c;市场上有8种可用的项目管理软件可以作为Monday.com的替代工具&#xff0c;分别是&#xff1a;Zoho Projects、Trello、Asana、Wrike、Basecamp、JIRA、Mi…

Hadoop-生产调优(更新中)

第1章 HDFS-核心参数 1.1 NameNode内存生产配置 1&#xff09;NameNode 内存计算 每个文件块大概占用 150 byte&#xff0c;一台服务器 128G 内存为例&#xff0c;能存储多少文件块呢&#xff1f; 128 * 1024 * 1024 * 1024 / 150byte ≈ 9.1 亿G MB KB Byte 2&#xff09…

【Redis】签到点赞和UV统计

Redis签到点赞和UV统计 点赞 点赞功能分析 需求&#xff1a; 同一个用户只能点赞一次&#xff0c;再次点击则取消点赞如果当前用户已经点赞&#xff0c;则点赞按钮高亮显示&#xff08;前端判断字段isLike属性&#xff09; 实现步骤&#xff1a; 利用Redis的set集合判断是…

安科瑞光伏、储能一体化监控及运维解决方案

上海安科瑞电气股份有限公司 胡冠楠 咨询家&#xff1a;“Acrelhgn”&#xff0c;了解更多产品资讯 前言 今年以来&#xff0c;在政策利好推动下光伏、风力发电、电化学储能及抽水蓄能等新能源行业发展迅速&#xff0c;装机容量均大幅度增长&#xff0c;新能源发电已经成为新…

Nicn的刷题日常之带空格直角三角形图案

1.题目描述 描述 KiKi学习了循环&#xff0c;BoBo老师给他出了一系列打印图案的练习&#xff0c;该任务是打印用“*”组成的带空格直角三角形图案。 输入描述&#xff1a; 多组输入&#xff0c;一个整数&#xff08;2~20&#xff09;&#xff0c;表示直角三角形直角边的长度&am…

STM32F407移植OpenHarmony笔记7

继上一篇笔记&#xff0c;成功启动了liteos_m内核&#xff0c;可以创建线程了&#xff0c;也能看到shell控制台了。 今天研究文件系统&#xff0c;让控制台相关文件命令如mkdir和ls能工作。 liteos_m内核支持fatfs和littlefs两个文件系统&#xff0c; fatfs适用于SD卡&#xff…

【Tomcat与网络6】 Tomcat是如何扩展Java线程池的?

目录 1.Java 的线程池 2.Tomcat 的线程池 学习Tomcat的时候&#xff0c;有很多绚丽的技术值得我们学习&#xff0c;但是个人认为Tomcat的线程池扩展是最值得研究的一个部分&#xff0c;线程池的应用太广了&#xff0c;也重要了&#xff0c;Java原生线程池的特征我相信很多人都…

【Redis】一文搞懂redis的所有知识点

目录 1. 什么是Redis&#xff1f;它主要用来什么的&#xff1f; 2.说说Redis的基本数据结构类型 2.1 Redis 的五种基本数据类型​编辑 2.2 Redis 的三种特殊数据类型 3. Redis为什么这么快&#xff1f;​编辑 3.1 基于内存存储实现 3.2 高效的数据结构 3.3 合理的数据编…

Python 中常用图像数据结构

&#xff08;原文&#xff1a;https://blog.iyatt.com/?p13222 &#xff09; 1 测试环境 Python 3.12.1 numpy 1.26.3 opencv-python 4.9.0.80 pillow 10.2.0 matplotlib 3.8.2 注&#xff1a; 基于 2022.1.16 和 2022.4.9 的三篇博文再次验证并重写&#xff0c;原文已删…

嵌入式学习第十五天

内存管理: 1.malloc void *malloc(size_t size); 功能: 申请堆区空间 参数: size:申请堆区空间的大小 返回值: 返回获得的空间的首地址 失败返回NULL 2.free void free(void *ptr); 功能: 释放堆区空间 注…

复刻桌面小电视【包含代码分析】

宗旨&#xff1a;开源、分享、学习、进步&#xff0c;生命不息&#xff0c;折腾不止。 复刻小电视 感谢各位大佬的开源项目&#xff0c;让我有了学习的机会&#xff0c;如果侵权&#xff0c;请联系我删除。本人能力有限&#xff0c;如果有什么不对的地方&#xff0c;欢迎指正…

Vue3-Composition-API(二)

一、computed函数使用 1.computed 在前面我们讲解过计算属性computed&#xff1a;当我们的某些属性是依赖其他状态时&#xff0c;我们可以使用计算属性来处理 在前面的Options API中&#xff0c;我们是使用computed选项来完成的&#xff1b; 在Composition API中&#xff0c…

《C程序设计》上机实验报告(四)之一维数组

1.运行程序 #include <stdio.h> void main( ) { int a[5],i,j; for(i1;i<5;i) a[i]0; for(i1;i<5;i) for(j1;j<5;j) a[j]a[i]1; printf("%d %d\n",a[0],a[3]); } 要求&#xff1a; &#xff08;1&#xff09;输入并调试上述源程序&#xff0c;…

安装 vant-ui 实现底部导航栏 Tabbar

本例子使用vue3 介绍 vant-ui 地址&#xff1a;介绍 - Vant 4 (vant-ui.github.io) Vant 是一个轻量、可定制的移动端组件库 安装 通过 npm 安装&#xff1a; # Vue 3 项目&#xff0c;安装最新版 Vant npm i vant # Vue 2 项目&#xff0c;安装 Vant 2 npm i vantlatest-v…

数据可视化 pycharts实现中国各省市地图数据可视化

自用版 数据格式如下&#xff1a; 运行效果如下&#xff1a; import pandas as pd from pyecharts.charts import Map, TreeMap, Timeline, Page, WordCloud from pyecharts import options as opts from pyecharts.commons.utils import JsCode from pyecharts.globals im…

api接口1688商品详情接口采集商品详情数据商品价格详情页数据可支持高并发调用演示示例

接入1688商品详情API接口的步骤如下&#xff1a; 注册账号&#xff1a;首先&#xff0c;你需要在1688开放平台注册一个账号。 创建应用&#xff1a;登录后&#xff0c;在控制台中找到“我的应用”&#xff0c;点击“创建应用”。 获取API密钥&#xff1a;创建应用后&#xff…

C语言数据结构之二叉树

少年恃险若平地 独倚长剑凌清秋 &#x1f3a5;烟雨长虹&#xff0c;孤鹜齐飞的个人主页 &#x1f525;个人专栏 &#x1f3a5;前期回顾-栈和队列 期待小伙伴们的支持与关注&#xff01;&#xff01;&#xff01; 目录 树的定义与判定 树的定义 树的判定 树的相关概念 树的运用…

网络工程师必学知识:2、IPv4和IPv6地址划分

网络工程师必学知识&#xff1a;2、IPv4和IPv6地址划分 1.概述&#xff1a;2.IPv4&#xff1a;地址划分&#xff1a;有类划分&#xff0c;无类划分。一、有类划分&#xff1a;分为5类。ABCDE&#xff0c;掩码分别位8、16、24、28、27取值范围&#xff1a;出类别bit不变&#xf…

我是赵士杰,自述我的 Java 之旅:四年编码,千言万语中成长

你好我的朋友&#xff0c;请先容许我作一个简单介绍&#xff1a;我是赵士杰&#xff0c;一名 Java 攻城狮&#xff0c;欢迎关注我的微信公众号【技术人阿杰】。 不知不觉中&#xff0c;我在撰写技术博客领域已经投入了四年的精力&#xff0c;这也让我从一个默默无名之辈成长为了…

种草日记|林曦老师的冬日好物分享

冬天将尽春天就要来了&#xff0c;换季的时候最容易引起皮肤干燥、头发毛躁不舒服的问题&#xff0c;今天就来说说林曦老师推荐的冬日护理爱用好物。大家都要“如婴儿乎”&#xff0c;照顾好自己哦&#xff5e;      1、Aco甘油保湿霜    Aco甘油保湿霜好大一罐&#x…