深入理解Python异常处理机制:助力你的自动化测试脚本

news2024/11/20 15:51:11

前言

 

前些天,公司准备使用开源BI工具superset,但部署成功后,连接阿里数仓获取表时,一直报错,苦于日志不详细,从日志中并没有看出哪里的问题,然后就拉源码进行调试,终于找到抛出异常的位置,但是当我打印这个异常的时候并没有异常信息输出,这促使我重新看一遍python的异常与错误处理。

try语句

try:    passexcept Exception as e:    print(e)finally:    print("Finally block")

图片


位置

图片

我们需要把更精确的except语句放到最前面,python的内置异常类之间是存在继承关系的,就拿我看superset源码中的例子

print(issubclass(NotImplementedError, RuntimeError)) # Trueprint(issubclass(RuntimeError, Exception)) # Trueprint(issubclass(Exception, BaseException)) # True

可以看到异常类的继承关系:BaseException -Exception -RuntimeError -NotImplementedError,当然,这只是我举的一个例子。

如果代码块中有多条except,异常匹配会按照从上到下的顺序进行。

如果把模糊不清的异常放在前面,就会导致下面的except不会触发,像下面这样

dic = {"name": "panda", "age": 18}try:    print(dic['sex'])except Exception as e:    print(f'exception: {e}')except KeyError:    print('sex not in dic')

这段代码会输出exception: 'sex',KeyError下面的代码永远不会执行。

图片

分支

图片

啥时候需要分支呢?和条件语句的分支一样吗?

当我们需要程序没有异常时,才执行之后的逻辑,我们就需要分支了。

先看看,不使用else,我们是这样实现的

dic = {"name": "panda", "age": 18}successed = Falsetry:    print(dic['sex'])    successed = Trueexcept KeyError:    print('sex not in dic')
if successed:    # 业务逻辑    print('successed')

这里我们定义了一个额外变量,来控制真正的业务逻辑是否执行。如果使用try语句中的else分支,代码会变的很简单,像这样

dic = {"name": "panda", "age": 18}try:    dic['name']except KeyError:    print('sex not in dic')else:    # 业务逻辑    print('successed')

代码运行会输出successed,虽然和条件语句中的else是同一个词,但意义不同,这里的else表示try语句未抛出异常时,才执行else分支下的内容。

当然,需要注意的是,和finally语句不同,如果程序遇到returnbreak语句,中断异常捕获,即使没有任何异常,else中的逻辑也不会执行,像这样

​​​​​​​

def demo():    try:        a = 1        return a    except Exception as e:        print(e)    else:        print('no error')    finally:        print('finally')
demo()

这段代码,会输出finally,虽然代码没有异常,但是因为有return,导致else中的代码不会执行。

图片

空raise语句

图片

 

抛出异常,交给上层处理

​​​​​​​

def demo():    try:        a = 1/0    except Exception as e:        raisedemo()

raise语句,会原封不动的重新抛出当前异常。这段代码执行结果为ZeroDivisionError: division by zero

图片

建议抛出异常不要返回错误

图片

之前写代码,我通常习惯将错误和结果一起返回,就是返回一个元组,包含返回结果和错误信息,然后通过错误信息来判断,进行后面的逻辑,像这样

​​​​​​​

MAX_LENGTH_OF_NAME = 12MAX_ITEMS_QUOTA = 10

class Item:    def __init__(self, name):        self.name = name

def create_item(name):    """接收名称,创建 Item 对象
    :return: (对象,错误信息),成功时错误信息为 ''    """    if len(name) > MAX_LENGTH_OF_NAME:        return None, 'name of item is too long'    return Item(name=name), ''

def create_from_input():    name = input()    item, err_msg = create_item(name)    if err_msg:        print(f'create item failed: {err_msg}')    else:        print('item<{name}> created')
create_from_input()

create_item()用来创建Item对象,函数内部进行长度判断逻辑,执行失败返回空字符串和错误信息组成的元组,在调用层create_from_input()来判断是否有错误信息,从而进行不同逻辑处理。刚开始感觉这样写还不错,后来代码量上来,都是这种写法,写的就懵了。

我们应该使用异常来进行错误处理,会优雅很多,像这样

​​​​​​​

# -*- coding: utf-8 -*-
MAX_LENGTH_OF_NAME = 12MAX_ITEMS_QUOTA = 10

def get_current_items():    return []

class Item:    def __init__(self, name):        self.name = name

class CreateItemError(Exception):    def __init__(self, *args, **kwargs):        pass

def create_item(name):    """创建一个新的 Item
    :raises: 当无法创建时抛出 CreateItemError    """    if len(name) > MAX_LENGTH_OF_NAME:        raise CreateItemError('name of item is too long')    return Item(name=name)

def create_from_input():    name = input()    try:        item = create_item(name)    except CreateItemError as e:        print(f'create item failed: {e}')    else:        print(f'item<{name}> created')
create_from_input()

这里我们自定义了异常错误类CreateItemError,这样create_item()函数只会返回Item类型或者抛出异常,清晰多了。

图片

with

图片

这并不陌生,我们通常操作一个文件时,会使用with来打开文件,像这样

​​​​​​​

with open('filename', 'r') as f:    pass

但并不是所有对象都能配合with使用,需要满足上下文管理器协议的对象才可以。

要满足上下文管理器,需要实现__enter__和__exit__两个魔法方法,__enter__在进入管理器时被调用,__exit__在退出管理器时调用。

图片

with使用案例

图片

代替finally清理资源,先来看看使用finally是这样实现的

​​​​​​​

conn = MySQLDB(host, port, user, password, database)try:    conn.execute_sql('select * from ...')except Exception as e:    print(f'Unable to use connection: {e}')finally:    conn.close()

这是我举的一个例子,MySQLDB是用来连接数据库的类,实现了execute_sql方法用来执行sql语句,最后关闭连接。

如果使用with会简洁很多,MySQLDB类中实现__enter__和__exit__两个魔法方法

​​​​​​​

class MySQLDB(SQLBase):    """MySQL DB table API"""
    def __init__(self, host: str, port: int, user: str, password: str, database: str, charset='utf8mb4'):        """        Connect to the MySQL database        :param host:        :param port:        :param user:        :param password:        :param database:        """        self.connection = pymysql.connect(host=host,                                          port=int(port),                                          user=user,                                          password=password,                                          database=database,                                          charset=charset,                                          cursorclass=pymysql.cursors.DictCursor)
    def __enter__(self):        return self.connection
    def __exit__(self, exc_type, exc_val, exc_tb):        self.connection.close()        return False
    def execute_sql(self, sql: str) -> None:        """        Execute SQL        pass

接下来,我们就可以使用with

​​​​​​​

with MySQLDB(host='127.0.0.1', port=3306, user='root', password='123456', database='test') as db:    db.execute_sql("")

忽略异常,有时候出现异常,并不会影响业务逻辑,但异常会阻碍程序运行,此时就需要try/except来捕获,但如果需要忽略的异常比较多,就有很多try/except,不优雅,我们可以直接实现一个上下文管理器来统一忽略异常,像这样

​​​​​​​

class ignore_closed:
    def __enter__(self):        pass
    def __exit__(self, exc_type, exc_value, traceback):        if exc_type == NotImplementedError:            return True        return False

在使用时,如果需要忽略NotImplementedError错误,就可以使用withwith ignore_closed():__exit__接收三个参数,with上下文内未抛出异常,解释器在执行__exit__方法时,exc_typeexc_valuetraceback这三个参数的值都是None,如果有异常抛出,这三个参数就是异常的具体内容:

  • exc_type :异常的类型

  • exc_value:异常对象

  • traceback:错误的堆栈对象

此时,如果__exit__返回True,异常就不会继续抛出,如果返回False,那异常就正常抛出。

当然,标准库模块contextlib里面的suppress函数,提供了忽略异常功能,可以直接使用

contextmanager装饰器

上面的例子可以看到,定义一个上下文管理器还是比较麻烦的,需要实现两个魔法方法。为了简化,python提供的更简便的装饰器:@contextmanager,像这样

​​​​​​​

from contextlib import contextmanager

@contextmanagerdef create_conn_obj():    conn = MySQLDB(host='127.0.0.1', port=3306, user='root', password='123456', database='test')    try:        yield conn    finally:        conn.close()

yield前面的逻辑会进入管理器时执行,就像__enter__,yield后面的逻辑会在退出管理器时执行,就像__exit__想要在上下文管理器内处理异常,必须使用 try包裹yield语句。

@contextmanager可以把一个生成器函数转化成上下文管理器。

图片

定位问题

图片

superset源码定位问题时,这样一段代码出现异常

​​​​​​​

try:    views = set(inspector.get_view_names(schema))except Exception as ex:    print(ex)    raise cls.get_dbapi_mapped_exception(ex) 

在这段代码中,当发生异常时,通过 print(ex) 语句打印异常信息是一个常见的做法。然而,并非所有类型的异常都包含可读的文本信息。

有些异常可能不会直接包含有用的信息,而是需要从其他属性中获取。因此,打印异常时可能看不到具体的错误信息。

为了更好地查看异常信息,可以尝试打印完整的异常堆栈信息,而不仅仅是异常对象本身。

可以修改代码如下:

​​​​​​​

try:    views = set(inspector.get_view_names(schema))except Exception as ex:    import traceback    traceback.print_exc()    raise cls.get_dbapi_mapped_exception(ex) 

通过使用 traceback.print_exc() 函数,可以打印完整的异常堆栈信息,包括异常的类型、消息和堆栈轨迹,这样可以更全面地了解发生了什么异常以及其详细信息。学到了,果然很快就解决问题了

图片

最后

图片

公司的BI工具总算可以正常使用了,感受到了看源码、改源码的快乐。

图片

END

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

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

相关文章

2024年计算机三级|数据库习题整理(自用③)

所有题目均来自【三级数据库技术基础题库】&#xff0c;此博客仅包含部分设计题与应用题&#xff0c;用于自主的回顾学习&#xff0c;仅供参考。 ER图绘制 代码补全 方案选择 &#xff08;1&#xff09;在不改变SQL语句和不对表做分区的情况下&#xff0c;可以为学生进出校的申…

自动驾驶感知新范式——BEV感知经典论文总结和对比(一)

自动驾驶感知新范式——BEV感知经典论文总结和对比&#xff08;一&#xff09; 博主之前的博客大多围绕自动驾驶视觉感知中的视觉深度估计&#xff08;depth estimation&#xff09;展开&#xff0c;包括单目针孔、单目鱼眼、环视针孔、环视鱼眼等&#xff0c;目标是只依赖于视…

【Python爬虫】将某网页中表格里的十六进制颜色值转换成十进制,再生成新表格

【需求】 在 https://www.cnblogs.com/heyang78/p/5712076.html 上有360种颜色及代码&#xff0c;但很遗憾没有十进制的RGB值&#xff0c;使用时需要自己转换一下&#xff0c;此过程依赖网络或计算器&#xff0c;颇为不便。因此&#xff0c;拟设计一爬虫将原有表格内容取出&am…

深度学习入门指南:从理论到实践

深度学习如何入门 深度学习是机器学习的一个分支&#xff0c;它通过模拟人脑神经网络的结构和功能来实现对数据的学习和理解。近年来&#xff0c;深度学习在图像识别、自然语言处理、语音识别等领域取得了显著的成果&#xff0c;越来越受到人们的关注。如果你想入门深度学习&a…

Ambari——编译——解决PowerShell中报错问题

您的支持是我继续创作与分享的动力源泉!!! 您的支持是我继续创作与分享的动力源泉!!! 您的支持是我继续创作与分享的动力源泉!!! 错误日志 因为在此系统上禁止运脚本。有关详细信息&#xff0c;请参阅 https:/go,microsoft,com/fwlink/?LinkID135170 中的 about_Execution…

Cesium安装部署运行

目录 1.简介 2.Cesium项目下载 3.Cesium项目运行 4.cesium运行 1.简介 Cesium是国外一个基于JavaScript编写的使用WebGL的地图引擎。Cesium支持3D,2D,2.5D形式的地图展示&#xff0c;可以自行绘制图形&#xff0c;高亮区域&#xff0c;并提供良好的触摸支持&#xff0c;且支…

安卓手机系统跳过app启动广告软件

跳过广告关于此应用声明&#xff1a; 应用利用了安卓系统的辅助功能API&#xff0c;可以读取您手机屏幕上显示的所有内容&#xff0c;并且可以以您的名义进行屏幕点击等操作。* 轻量无广告&#xff0c;不联网&#xff0c;也不需要任何权限&#xff1b;* 请务必在系统设置中开启…

es bulk批量操作简单实例

&#xff08;1&#xff09;定义 bulk允许在单个步骤中进行多次create、index、update或delete请求。 bulk与其他的请求体格式稍有不同&#xff0c;如下所示&#xff1a; { action: { metadata }}\n { request body }\n { action: { metadata }}\n { request body …

C++多态机制详解(多态实现原理,单继承和多继承时虚函数表,菱形继承时的虚函数表原理)

文章目录 多态的定义多态的实现1.多态实现的两个必要条件2.什么是虚函数3.重写的条件4.多态实现代码5.重写的两个例外 C11引入的final和override关键字重载&#xff0c;重写&#xff08;覆盖&#xff09;&#xff0c;隐藏&#xff08;重定义&#xff09;抽象类接口继承和实现继…

关于JAVA8的Lambda表达式

1. 水在前面 这个礼拜忽然心血来潮把Lambda表达式学习了一遍&#xff0c;发现这玩意跟原来想象的好像不是一个东西&#xff0c;写个学习心得供以后复习用。还是那句话&#xff0c;这篇水文不能让你完全掌握&#xff0c;只是用来给我自己温习用的&#xff0c;或者也可以作为小伙…

DXP学习2- 绘制电气图【实验】

目录 一、实验目的 二、实验原理 1、创建一个新的项目文件。 2、新建原理图文件 3、设置原理图选项 4、放置元器件 5、其他电路元素的放置 6、对所有电路元素属性参数值的修改 三、实验设备 四、实验内容 1、绘制实验图2-1 元器件所在位置&#xff1a; 1&#xff0c;…

四、Elasticsearch 进阶

自定义目录 4.1 核心概念4.1.1 索引&#xff08;Index&#xff09;4.1.2 类型&#xff08;Type&#xff09;4.1.3 文档&#xff08;Document&#xff09;4.1.3 字段&#xff08;Field&#xff09;4.1.5 映射&#xff08;Mapping&#xff09;4.1.6 分片&#xff08;Shards&#…

基于java+springboot+vue实现的游戏账号估价交易平台(文末源码+Lw+ppt)23-555

摘 要 系统根据现有的管理模块进行开发和扩展&#xff0c;采用面向对象的开发的思想和结构化的开发方法对游戏账号估价交易的现状进行系统调查。采用结构化的分析设计&#xff0c;该方法要求结合一定的图表&#xff0c;在模块化的基础上进行系统的开发工作。在设计中采用“自…

【计算机网络篇】数据链路层(1)数据链路层的地位,问题

文章目录 &#x1f354;数据链路层在网络体系结构中的地位&#x1f354;链路&#xff0c;数据链路&#xff0c;帧&#x1f354;数据链路层的三个重要问题&#x1f95a;封装成帧和透明传输&#x1f95a;差错检测&#x1f95a;可靠传输 &#x1f354;数据链路层在网络体系结构中的…

Tableau学习——范围-线图、倾斜图

1范围-线图&#xff08;人工接听数据&#xff09; 范围-线图&#xff1a;将整体及个体数据特征&#xff08;均值、最值等&#xff09;都展示出来了 筛选出某个员工 &#xff08;1&#xff09;创建计算字段来表示均值、最大值、最小值 &#xff08;2&#xff09;数据处理好后&…

Day61:WEB攻防-PHP反序列化原生类TIPSCVE绕过漏洞属性类型特征

知识点&#xff1a; 1、PHP-反序列化-属性类型&显示特征 2、PHP-反序列化-CVE绕过&字符串逃逸 3、PHP-反序列化-原生类生成&利用&配合 补充&#xff1a;如果在 PHP 类中没有实现某个魔术方法&#xff0c;那么该魔术方法在相应的情况下不会被自动触发。PHP 的魔…

路桥公司知识竞赛活动方案

一、参赛对象 龙建路桥股份有限公司权属企业 二、组织单位 主办单位&#xff1a;龙建路桥股份有限公司委员会 承办单位&#xff1a;黑龙江省龙建路桥第二工程有限公司委员会 三、活动时间 11月&#xff08;具体时间另行通知&#xff09; 四、活动地点 龙建松北综合经营生产中心…

基于Java中的SSM框架实现图书仓储管理系统项目【项目源码+论文说明】计算机毕业设计

基于Java中的SSM框架实现图书仓储管理系统演示 摘要 随着社会经济的迅速发展和科学技术的全面进步&#xff0c;计算机事业的飞速发展&#xff0c;以计算机与通信技术为基础的信息系统正处于蓬勃发展的时期&#xff0c;随着经济文化水平的显著提高&#xff0c;人们对生活质量及…

Token的详解

Token的详解 文章目录 Token的详解前言:简介:使用token&#xff1a; 前言: 为什么会用到Token&#xff0c;因为cookie和session一些自身的缺点&#xff0c;限制了一些功能的实现&#xff0c;比如&#xff1a; cookie&#xff1a;优点是节省服务器空间&#xff0c;缺点不安全。…

如何监控企业微信聊天记录内容

假如说老板可以查看到你的微信聊天记录&#xff0c;那么此时此刻的你&#xff0c;会不会瑟瑟发抖&#xff1f; 其实不用啦&#xff0c;监控企业微信聊天记录&#xff0c;也是需要员工个人同意的。 下面我介绍两种方法&#xff0c;看看你属于哪种 方法一 企业微信自带功能 …