深入理解Python中的try-except语句,避免代码崩溃的噩梦

简介: 深入理解Python中的try-except语句,避免代码崩溃的噩梦

不管是开发Python、Java、JavaScript程序,还是写C++、PHP、GO、Ruby代码。都离不开try ... except异常处理。至于为什么,一切为了程序/产品/项目的稳定运行。如果一个程序项目或者说小一点一个文件中没有一个try-except处理,那么这个程序是有漏洞的、是不稳定的,很容易就会崩溃的。


而我们经常在代码开发时,特别是框架项目开发时,多个模块(文件)是会存在调用的情况,有单向的,也有双向的。而在此时如果程序发生异常,try-except只会基于当前位置进行信息打印(我说的是Except as e),很难去定位到模块调用,也就是源头文件的位置。今天分享的内容就是如何定位源头文件及行数。

Python try-except例子:


在这个例子中,我们将除法运算代码封装到了一个名为divide的函数中。函数的第一个参数a表示被除数,第二个参数b表示除数。在函数体中,我们使用try except语句对除以0的异常进行了处理。如果程序没有遇到ZeroDivisionError异常,那么函数将成功计算结果并返回。如果程序遇到ZeroDivisionError异常,我们将输出具体的错误信息并返回None。

def divide(a, b):
    try:
        c = a / b
        return c
    except ZeroDivisionError as e:
        print(e)
        return None
# 调用函数进行除法运算
result = divide(10, 0)
print(result)

输出结果:

Divide by zero error

定位源头文件(报错源)


大多时候我们在大工程或者复杂程序里,仅仅打印e只能简单的显示出程序出错的信息,有时我们仅仅根据这个信息很难定位,错误究竟发生在哪一个文件、哪一行代码,这时候比较笨的调试方法就是去掉try-except再跑一次让程序崩溃显示出具体的信息。


而比较快速便捷的定位错误信息就是定位到发生错误具体的行和文件,其实我们可以利用错误对象e,它其实是有相关的属性可调用的。

def divide(a, b):
    try:
        c = a / b
        return c
    except ZeroDivisionError as e:
        # 通过e的属性打印具体文件名称和行号
        print(f"Error Occurred At, 
              f"File: {e.__traceback__.tb_frame.f_code.co_filename}, "
              f"Line: {e.__traceback__.tb_lineno}")
        print("Divide by zero error")
        return None
# 调用函数进行除法运算
result = divide(10, 0)
print(result)

输出结果:

Error Occurred At, File: 文件路径, Line: 错误行号
Divide by zero error
None

或者还可以借助内置模块traceback实现

import traceback
import sys
def divide(a, b):
    try:
        c = a / b
        return c
    except ZeroDivisionError as e:
        # 借助traceback实现 
        tb_list = traceback.extract_tb(sys.exc_info()[2])
        filename, line_no, func_name, code_str = tb_list[-1]
        print(f"Error Occurred At, 
              f"File: {filename}, "
              f"Line: {line_no}")
        print("Divide by zero error")
        return None
# 调用函数进行除法运算
result = divide(10, 0)
print(result)

进阶用法


这个时候还会有一些问题,就是源头报错文件是一个三方库或者内置库,比如文件位置是python环境site-packages中,那我们打印出来并没有太大的用处,我们想打印的是我们自己写的代码报错位置,而不是内置模块或安装的第三方模块报错的位置信息,只有找到自定义代码位置,才好进行优化修正。

可以使用traceback.extract_stack()替换traceback.extract_tb(),以排除第三方库的源代码位置。

import traceback
import sys
def divide(a, b):
    try:
        c = a / b
        return c
    except ZeroDivisionError as e:
        # 借助traceback实现 
        tb_list = traceback.extract_stack()[:-1]  # 最后一项是当前调用的位置,而不是异常发生的位置
        filename, line_no, func_name, code_str = tb_list[-1]
        print(f"Error Occurred At, 
              f"File: {filename}, "
              f"Line: {line_no}")
        print("Divide by zero error")
        return None
# 调用函数进行除法运算
result = divide(10, 0)
print(result)

使用traceback.extract_stack()方法获取异常发生的完整堆栈信息。然后,使用list slicing来去掉最后一个元素,因为这个元素代表当前调用的位置,而不是异常发生的位置。最后一个元素通常是包含你的调用代码的位置,所以你不想把它包含进来。然后,从可用的堆栈信息中提取最后一个源代码文件名、行号、函数名和代码行来确定异常发生的位置。


这个方法不会包含第三方库的源代码位置,并且只会提取与你的代码相关的源代码位置。


还可以使用Python标准库中的inspect模块的currentframe()函数来获取当前帧。然后,你可以使用traceback.extract_stack()方法来提取堆栈信息,然后使用堆栈信息来逐步比较源文件路径,以找到与当前工程相关的源文件路径。

import os
import inspect
import traceback
def get_project_traceback():
    # 获取当前帧
    frame = inspect.currentframe()
    # 提取堆栈信息(前两个元素是filter和get_project_traceback函数本身的位置)
    tb_list = traceback.extract_stack(frame)[2:]
    # 找到与当前工程相关的源文件路径
    for filename, line_no, func_name, code_str in reversed(tb_list):
        if os.getcwd() in filename:
            return filename, line_no, func_name, code_str
    # 如果都没有找到,则返回最后一个源文件
    filename, line_no, func_name, code_str = tb_list[-1]
    return filename, line_no, func_name, code_str
def divide(a, b):
    try:
        c = a / b
        return c
    except ZeroDivisionError as e:
        filename, line_no, func_name, code_str = get_project_traceback()
        print(f"Error Occurred At, 
              f"File: {filename}, "
              f"Line: {line_no}")
        print("Divide by zero error")
        return None
# 调用函数进行除法运算
result = divide(10, 0)
print(result)

上述代码中,get_project_traceback函数首先使用inspect.currentframe()函数获取当前帧,然后使用traceback.extract_stack(frame)方法提取堆栈信息,前两个元素是filter和get_project_traceback函数本身的位置,所以从第3个元素开始获取。接着,函数使用os.getcwd()来获取当前工程的路径,并逐个比较每个源文件路径,找到与当前工程相关的源文件路径。如果都没有找到,则返回最后一个源文件。


在主代码块中,通过调用get_project_traceback函数来获取与当前工程相关的堆栈信息。当一个异常发生时,该代码段将获取该异常的信息并使用get_project_traceback函数来打印与当前工程相关的堆栈信息。

相关文章
|
2月前
|
运维 监控 算法
时间序列异常检测:MSET-SPRT组合方法的原理和Python代码实现
MSET-SPRT是一种结合多元状态估计技术(MSET)与序贯概率比检验(SPRT)的混合框架,专为高维度、强关联数据流的异常检测设计。MSET通过历史数据建模估计系统预期状态,SPRT基于统计推断判定偏差显著性,二者协同实现精准高效的异常识别。本文以Python为例,展示其在模拟数据中的应用,证明其在工业监控、设备健康管理及网络安全等领域的可靠性与有效性。
619 13
时间序列异常检测:MSET-SPRT组合方法的原理和Python代码实现
|
2月前
|
SQL 自然语言处理 数据库
【Azure Developer】分享两段Python代码处理表格(CSV格式)数据 : 根据每列的内容生成SQL语句
本文介绍了使用Python Pandas处理数据收集任务中格式不统一的问题。针对两种情况:服务名对应多人拥有状态(1/0表示),以及服务名与人名重复列的情况,分别采用双层for循环和字典数据结构实现数据转换,最终生成Name对应的Services列表(逗号分隔)。此方法高效解决大量数据的人工处理难题,减少错误并提升效率。文中附带代码示例及执行结果截图,便于理解和实践。
|
9天前
|
数据采集 运维 API
把Postman调试脚本秒变Python采集代码的三大技巧
本文介绍了如何借助 Postman 调试工具快速生成 Python 爬虫代码,并结合爬虫代理实现高效数据采集。文章通过“跨界混搭”结构,先讲解 Postman 的 API 调试功能,再映射到 Python 爬虫技术,重点分享三大技巧:利用 Postman 生成请求骨架、通过 Session 管理 Cookie 和 User-Agent,以及集成代理 IP 提升稳定性。以票务信息采集为例,展示完整实现流程,探讨其在抗封锁、团队协作等方面的价值,帮助开发者快速构建生产级爬虫代码。
把Postman调试脚本秒变Python采集代码的三大技巧
|
5月前
|
开发框架 数据建模 中间件
Python中的装饰器:简化代码,增强功能
在Python的世界里,装饰器是那些静悄悄的幕后英雄。它们不张扬,却能默默地为函数或类增添强大的功能。本文将带你了解装饰器的魅力所在,从基础概念到实际应用,我们一步步揭开装饰器的神秘面纱。准备好了吗?让我们开始这段简洁而富有启发性的旅程吧!
93 6
|
1月前
|
开发框架 Java .NET
Python中main函数:代码结构的基石
在Python中,`main`函数是程序结构化和模块化的重要组成部分。它实现了脚本执行与模块导入的分离,避免全局作用域污染并提升代码复用性。其核心作用包括:标准化程序入口、保障模块复用及支持测试驱动开发(TDD)。根据项目复杂度,`main`函数有基础版、函数封装版、参数解析版和类封装版四种典型写法。 与其他语言相比,Python的`main`机制更灵活,支持同一文件作为脚本运行或模块导入。进阶技巧涵盖多文件项目管理、命令行参数处理、环境变量配置及日志集成等。此外,还需注意常见错误如全局变量污染和循环导入,并通过延迟加载、多进程支持和类型提示优化性能。
119 0
|
3月前
|
数据采集 供应链 API
实战指南:通过1688开放平台API获取商品详情数据(附Python代码及避坑指南)
1688作为国内最大的B2B供应链平台,其API为企业提供合法合规的JSON数据源,直接获取批发价、SKU库存等核心数据。相比爬虫方案,官方API避免了反爬严格、数据缺失和法律风险等问题。企业接入1688商品API需完成资质认证、创建应用、签名机制解析及调用接口四步。应用场景包括智能采购系统、供应商评估模型和跨境选品分析。提供高频问题解决方案及安全合规实践,确保数据安全与合法使用。立即访问1688开放平台,解锁B2B数据宝藏!
|
3月前
|
API 开发工具 Python
【Azure Developer】编写Python SDK代码实现从China Azure中VM Disk中创建磁盘快照Snapshot
本文介绍如何使用Python SDK为中国区微软云(China Azure)中的虚拟机磁盘创建快照。通过Azure Python SDK的Snapshot Class,指定`location`和`creation_data`参数,使用`Copy`选项从现有磁盘创建快照。代码示例展示了如何配置Default Azure Credential,并设置特定于中国区Azure的`base_url`和`credential_scopes`。参考资料包括官方文档和相关API说明。
|
4月前
|
存储 缓存 Java
Python高性能编程:五种核心优化技术的原理与Python代码
Python在高性能应用场景中常因执行速度不及C、C++等编译型语言而受质疑,但通过合理利用标准库的优化特性,如`__slots__`机制、列表推导式、`@lru_cache`装饰器和生成器等,可以显著提升代码效率。本文详细介绍了这些实用的性能优化技术,帮助开发者在不牺牲代码质量的前提下提高程序性能。实验数据表明,这些优化方法能在内存使用和计算效率方面带来显著改进,适用于大规模数据处理、递归计算等场景。
124 5
Python高性能编程:五种核心优化技术的原理与Python代码
|
5月前
|
Python
课程设计项目之基于Python实现围棋游戏代码
游戏进去默认为九路玩法,当然也可以选择十三路或是十九路玩法 使用pycharam打开项目,pip安装模块并引用,然后运行即可, 代码每行都有详细的注释,可以做课程设计或者毕业设计项目参考
125 33
|
5月前
|
JavaScript API C#
【Azure Developer】Python代码调用Graph API将外部用户添加到组,结果无效,也无错误信息
根据Graph API文档,在单个请求中将多个成员添加到组时,Python代码示例中的`members@odata.bind`被错误写为`members@odata_bind`,导致用户未成功添加。
82 10