我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Python实现自定义异常堆栈信息的示例代码

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Python实现自定义异常堆栈信息的示例代码

当我们的程序报错时,解释器会将整个异常的堆栈信息全部输出出来,举个例子:

def foo():
    raise RuntimeError("抛一个异常")

def bar():
    foo()

def main():
    bar()

main()

如果执行这段代码,会得到以下报错信息:

解释器会将异常产生的整个调用链都给打印出来,那么问题来了,我们能不能自定义这些报错信息呢?

答案是可以的,我们只要拿到这些报错信息,然后再进行修改即可。那么如何才能拿到呢?显然需要借助于 traceback 对象。

import sys

def foo():
    raise RuntimeError("抛一个异常")

def bar():
    foo()

def main():
    bar()

try:
    main()
except Exception:
    # sys.exc_info() 返回一个元组,里面有三个元素
    # 分别是:异常类型、异常值、异常的堆栈信息
    exc_type, exc_val, exc_tb = sys.exc_info()

    print(exc_type)
    print(exc_val, type(exc_val))
    print(exc_tb)
    """
    <class 'RuntimeError'>
    抛一个异常 <class 'RuntimeError'>
    <traceback object at 0x7fe35811de40>
    """

每一个函数在运行时都会创建一个栈帧,栈帧在 CPython 里面由 PyFrameObject 结构体表示。同时每个栈帧都会对应一个 PyTracebackObject,也就是异常堆栈,即代码中的 exc_tb。

上面的代码在执行函数 foo 的时候出现了异常,那么解释器会创建对应的 traceback 对象,由于 foo 内部没有异常捕获,因此会回退到上一级栈帧,继续寻找异常捕获逻辑。而 foo 的上一级栈帧是 bar 的栈帧,因此解释器同样会基于 bar 的栈帧创建 traceback 对象,并且 bar 的 traceback 对象的 tb_next 指向 foo 的 traceback 对象。

就这样栈帧一层层的回退,整个过程我们称之为栈帧展开。在栈帧展开的过程中,解释器不断地创建与各个栈帧对应的 traceback,并将其链接成链表。

如果回退到最外层(模块)也没有找到异常捕获逻辑,那么解释器就要输出异常信息了,从模块对应的 traceback 开始不断遍历,将每一层信息都输出出来,就是我们看到的样子。

而我们也可以在拿到 traceback 之后,手动输出出来。

import sys
import traceback

def foo():
    raise RuntimeError("抛一个异常")

def bar():
    foo()

def main():
    bar()

try:
    main()
except Exception:
    exc_type, exc_val, exc_tb = sys.exc_info()

    error_msg = "".join(
        traceback.format_exception(
            exc_type, exc_val, exc_tb)
    )
    print(error_msg)
    """
    Traceback (most recent call last):
      File "/Users/satori/Desktop/project/main.py", line 14, in <module>
        main()
      File "/Users/satori/Desktop/project/main.py", line 11, in main
        bar()
      File "/Users/satori/Desktop/project/main.py", line 8, in bar
        foo()
      File "/Users/satori/Desktop/project/main.py", line 5, in foo
        raise RuntimeError("抛一个异常")
    RuntimeError: 抛一个异常
    """

Python 有一个标准模块也叫 traceback,使用它的 format_exception 函数,我们可以拿到格式化后的异常堆栈信息,而该函数接收的参数正是 exc_type, exc_val, exc_tb。

虽然我们拿到了异常的堆栈信息,但是还不够,因为这里是通过手动捕获异常的方式。而在生产上,很多时候我们并不知道哪里会抛出异常,所以我们需要在不使用异常捕获逻辑的前提下,自动捕获异常。是不是有点绕了呢?我们举个例子。

import sys

def catch(exc_type, exc_val, exc_tb):
    """
    光看名字,就应该知道这三个参数的含义了
    """
    print("报错啦")
    print(exc_val)

# 之后当出现异常的时候,如果没有异常捕获逻辑
# 就会执行 catch 函数
sys.excepthook = catch

raise ZeroDivisionError("除零错误")
"""
报错啦
除零错误
"""

代码中我们 raise 了一个异常,默认情况下解释器应该将异常输出到 stderr 当中,然后中止运行。但是我们替换了 sys.excepthook,那么在出现异常的时候,解释器会去执行我们这里替换之后的 catch 函数,并自动将 exc_type, exc_val, exc_tb 作为参数传进去。

而一旦 cache 函数执行完毕,程序就结束了。

那么接下来我们就可以对异常输出进行改造了,至于怎么改,完全由你来决定,我们这里给个示例。

import sys
from io import StringIO
import re
import traceback
from rich import print

def catch(exc_type, exc_val, exc_tb):
    buf = StringIO()
    # 得到一个列表
    errors = traceback.format_exception(
        exc_type, exc_val, exc_tb)
    buf.write(f"[blue]程序出现异常啦, 客官请看下面:\n\n")
    # 遍历
    i = 1
    while i < len(errors):
        match = re.search(r'File "(.+?)", line (\d+), in (.+)\s*(.*)',
                          errors[i].strip())
        if match is not None:
            file_path, lineno, where, reason = match.groups()
            buf.write(
                f"在文件 [red bold]{file_path}[/red bold] "
                f"[green bold]{where}[/green bold] 的"
                f"第 [yellow bold]{lineno}[/yellow bold] 行\n"
            )
            buf.write(f"执行了 {reason.strip()}\n\n")
        i += 1
    buf.write(f"[cyan bold]{errors[-1].strip()}")
    print(buf.getvalue())

sys.excepthook = catch

def foo():
    # raise a exception
    {} + ()

def bar():
    foo()

def main():
    bar()

main()

我们执行这段代码,看看它的错误输出是什么样子。

怎么样,是不是很好玩呢?只要把 sys.excepthook = cache 这段逻辑加载到程序中,我们就可以自定义异常显示信息了。

当然啦,根据异常的不同,解释器可能输出更复杂的信息,所以我们上面的代码并不完善,但你可以根据实际情况进行修改。只要知道如何获取解释器输出的异常信息,以及 sys.excepthook 的用法就足够了。

最后再强调一下,解释器在发现异常的时候,会立即调用 sys.excepthook。而一旦调用结束,整个程序就结束了。

以上就是Python实现自定义异常堆栈信息的示例代码的详细内容,更多关于Python异常堆栈信息的资料请关注编程网其它相关文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Python实现自定义异常堆栈信息的示例代码

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

C#实现自定义屏保的示例代码

这篇文章主要为大家详细介绍了如何利用C#实现自定义屏保的功能,文中的示例代码讲解详细,对我们学习C#有一定的帮助,感兴趣的小伙伴可以跟随小编一起了解一下
2022-12-31

Python实现计算信息熵的示例代码

信息熵(information entropy)是信息论的基本概念。描述信息源各可能事件发生的不确定性。本文将通过Python实现信息熵的计算,感兴趣的可以了解一下
2022-12-26

Python实现自动添加脚本头信息的示例代码

前言 每个人写脚本时的格式都会有所不同,有的会注明脚本本身的一些信息,有的则开门见山,这在小团队里其实没什么,基本别人做什么你也都知道,但如果放到大的团队就比较麻烦了,因为随着人数的增多,脚本成指数增长,如果每个人风格不统一,到最后就会造成
2022-06-04

Android实现自定义加载框的代码示例

App在与服务器进行网络交互的时候,需要有一个提示的加载框,如图:此时我们可以自定义一个加载中的对话框,代码如下:public class LoadingDialog extends Dialog { private static fin
2022-06-06

Python实现搜索GoogleScholar论文信息的示例代码

这篇文章主要为大家详细介绍了如何利用Python实现搜索GoogleScholar论文信息的功能,文中的示例代码讲解详细,需要的可以参考一下
2023-03-06

C++实现自定义撤销重做功能的示例代码

在使用c++做界面开发的时候,尤其是实现白板功能时需要自己实现一套撤销重做功能.如果是qt则有QUndoable对象,可以直接拿来用。但是如果是使用gdi绘图,则可能需要自己实现了。本文就来用C++实现自定义撤销重做功能,需要的可以参考一下
2022-12-15

Asp.net Core中实现自定义身份认证的示例代码

Asp.Net Core中虽然集成了许多常用的身份认证,但很多时候,我们还是需要实现自己的身份认证接口,本文这里就简单的介绍下如何实现自定义身份认证接口。 首先写一个简单的接口。[Authorize] [HttpGet] public ob
2022-06-07

Python实现学生信息管理系统的示例代码

夏天是用来告别的季节,因为毕业总在七月。那么七月之前的季节是用来干嘛的呢?当然是用来做毕业设计的啦!本文为大家准备了两个版本的学生信息管理系统,希望对大家有所帮助
2023-02-15

Android实现自定义圆角对话框Dialog的示例代码

前言: 项目中多处用到对话框,用系统对话框太难看,就自己写一个自定义对话框。 对话框包括:1、圆角2、app图标 , 提示文本,关闭对话框的"确定"按钮 难点:1、对话框边框圆角显示2、考虑到提示文本字数不确定,在不影响美观的情况下,需
2022-06-06

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录