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:
    # 异常类型是 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、小结

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

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

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

相关文章

vue前端html导出pdf

package.json中添加依赖 调用方&#xff1a; import htmlToPdf from ../../../utils/file/htmlToPdf.js// 下载方法&#xff0c;pdfDownloadDpi为onClickDownLoad() {htmlToPdf.getPdf(标题1, jsfgyzcpgxmShow, this.pdfDownloadDpi)}htmlToPdf.js // 页面导出为pdf格式 imp…

Backtrader 文档学习- Broker - Cheat-On-Open

Backtrader 文档学习- Broker - Cheat-On-Open 1.概述 V1.9.44.116增加了Cheat On Open的支持。对于全押的人来说&#xff0c;这似乎是一个必需的功能&#xff0c;用bar的收盘价后进行计算&#xff0c;希望与开盘价相匹配。 当开盘价差距&#xff08;上涨或下跌&#xff0c;取…

杂题——试题-算法训练-P0602

分析&#xff1a; 把要重排序的数字转成数组对数组进行排序&#xff0c;从小到大排序数组转成字符串&#xff0c;字符串转成数字&#xff0c;得到最小数再把最小数的字符串反转&#xff0c;得到最大数注意&#xff1a; 在java语言中&#xff0c;如果使用Arrays.toString(digits…

DevOps系列文章之 Git命令:过滤日志

使用git log命令查找特定提交历史的高级技巧。其中的一些技巧配合格式化日志命令使用有奇效。 按照数量过滤 使用git log命令时最基本的过滤方式就是按照可以显示的日志条数进行过滤。如果你只对最近几次提交感兴趣&#xff0c;这会节省在页面上查看所有提交的麻烦。 git lo…

成熟的汽车制造供应商协同平台 要具备哪些功能特性?

汽车行业是一个产业链长且“重”的行业&#xff0c;整个业务流程包括了研发、设计、采购、库存、生产、销售、售后等一系列环节&#xff0c;在每一个环节都涉及到很多信息交换的需求。对内要保证研发、采购、营销等业务环节信息流通高效安全&#xff0c;对外要与上、下游合作伙…

springboot-前后端分离——第一篇

本篇主要对前后端分离的一些基础知识进行总结&#xff0c;主要对HTTP请求协议、HTTP响应格式、Http协议解析等进行总结。重点在于简单了解前端如何向服务端发送请求&#xff0c;服务端如何接收请求并返回响应结果。 一、简单案例&#xff1a; 首先创建一个springboot项目&…

使用pygame建立一个简单的使用键盘方向键移动的方块小游戏

import pygame import sys# 初始化pygame pygame.init()# 设置窗口大小 screen_size (640, 480) # 创建窗口 screen pygame.display.set_mode(screen_size) # 设置窗口标题 pygame.display.set_caption("使用键盘方向键移动的方块的简单小游戏")# 设置颜色 bg_colo…

帅气的性能监控平台Grafana(Windows下使用Grafana监控系统指标与GPU指标)

帅气的性能监控平台Grafana&#xff08;Windows下使用Grafana监控系统指标与GPU指标&#xff09; 前情提要 系统环境准备 windows_exporter下载 nvidia_gpu_exporter下载 prometheus下载 Grafana下载 安装指导 windows_exporter安装与nvidia_gpu_exporter安装 promethe…

ApacheNginx配置ssl证书

一、Apache配置ssl Linux版本&#xff1a;CentOS Linux release 7.9.2009 (Core) Apache版本&#xff1a;Apache/2.4.6 (CentOS) 1、安装Apache&#xff08;使用默认yum源&#xff09; [root10-35-1-25 ~]# yum -y install httpd2、查Apache版本&启动Apache [root10-35-…

深度解读NVMe计算存储协议-2

近日&#xff0c;NVME协议组织为了解决这些性能问题并为供应商提供标准化机制&#xff0c;在其架构中集成优化的计算功能&#xff0c;开发了NVM Express (NVMe) 计算存储特性。 计算存储的核心特性包括两个命令集&#xff1a;计算程序集和子系统本地内存。 其中&#xff0c;计算…

postgresql|数据库|pg_repack插件的部署和使用

一&#xff0c; 表和索引的膨胀现象 Postgres SQL 实现的MVCC的机制不同于 oracle &#xff0c; mysql innodb 的 undo tablespace 的机制。 表上所用的更新和删除等操作的行为&#xff0c;都不会实际的删除或修改&#xff0c;而是标记为死元祖 &#xff08;dead rows or dead…

非鸿蒙官方低代码源码生成器

介绍 鸿蒙低代码可视化开发神器快速对鸿蒙ArkUI生成源码&#xff0c;结合类似小程序类似设计&#xff0c;页面设计底部菜单&#xff0c;支持宫格组件、轮播图、图文列表、图片组件、文本内容组件&#xff0c;快速对接第三方HttpApi。通过鸿蒙扩展axios扩展库加载数据源&#x…

jmeter+nmon+crontab简单的执行接口定时压测

一、概述 临时接到任务要对系统的接口进行压测&#xff0c;上面的要求就是&#xff1a;压测&#xff0c;并发2000 在不熟悉系统的情况下&#xff0c;按目前的需求&#xff0c;需要做的步骤&#xff1a; 需要有接口脚本需要能监控系统性能需要能定时执行脚本 二、观察 >…

Spring的事件监听机制

这里写自定义目录标题 1. 概述&#xff08;重点&#xff09;2. ApplicationEventMulticaster2.1 SimpleApplicationEventMulticaster2.2 AbstractApplicationEventMulticaster 3. ApplicationListener3.1 注册监听器3.2 自定义 4. SpringApplicationRunListeners 1. 概述&#…

协会认证!百望云荣获信创工委会年度“卓越贡献成员单位”称号

当前&#xff0c;新一轮科技革命和产业变革正加速重塑全球经济结构&#xff0c;强化企业科技创新的主体地位&#xff0c;推动创新链、产业链、人才链深度融合&#xff0c;加快科技成果产业化进程至关重要。 近日&#xff0c;中国电子工业标准化技术协会信息技术应用创新工作委员…

对付勒索病毒,复杂的往往无法落地

一道道复杂门墙防护安全&#xff0c; 还是一个精密的锁更安全&#xff1f; &#x1f447;&#x1f447;&#x1f447; 在网络数据安全问题频发的当下&#xff0c;除了常规的备份、灾备措施以外&#xff0c;企业是否有做好应对最坏情况的准备&#xff1f;一旦病毒绕过了一道道…

shell - 免交互

一.Here Document 免交互 1. 交互的概念 交互&#xff1a;当计算机播放某多媒体程序的时候&#xff0c;编程人员可以发出指令控制该程序的运行&#xff0c;而不是程序单方面执行下去&#xff0c;程序在接受到编程人员相应的指令后而相应地做出反应。 对于Linux操作系统中&…

ztest中ddof起什么作用

⭐️ statsmodels 中 ztest 基本使用 statsmodels 也是一个强大的统计分析库&#xff0c;提供了丰富的统计模型和检验功能。对于 Z 检验&#xff0c;statsmodels 提供了 ztest 函数。 以下是使用 statsmodels 进行 Z 检验的示例&#xff1a; from statsmodels.stats.weights…

ElementUI 组件:Container 布局容器

ElementUI安装与使用指南 Container 布局容器 点击下载learnelementuispringboot项目源码 效果图 el-container.vue&#xff08;Container 布局容器&#xff09;页面效果图 项目里el-container.vue代码 <script> import PagePath from "/components/PagePat…

[NOIP2011 提高组] 聪明的质监员

[NOIP2011 提高组] 聪明的质监员 题目描述 小T 是一名质量监督员&#xff0c;最近负责检验一批矿产的质量。这批矿产共有 n n n 个矿石&#xff0c;从 1 1 1 到 n n n 逐一编号&#xff0c;每个矿石都有自己的重量 w i w_i wi​ 以及价值 v i v_i vi​ 。检验矿产的流程…