我什么时候应该声明自定义异常?

2024-01-03

我想引发异常来传达一些消息和与错误相关的值。我想知道什么时候声明自定义异常与使用内置异常最合适。

我见过很多这样的例子this https://dbader.org/blog/python-custom-exceptions还有很多人喜欢在其他网站上推荐它。

class NameTooShortError(ValueError):
    pass

def validate(name):
    if len(name) < 10:
        raise NameTooShortError(name)

我更倾向于编写如下代码:

def validate(name):
    if len(name) < 10:
        raise ValueError(f"Name too short: {name}")

我的本能是仅在需要将复杂或特定信息存储在异常实例中时才声明自定义异常。声明空类对我来说似乎是错误的。


有两个问题合并为一个:我应该多久使用一次自定义异常(以免过度使用它们)? and 我真的应该更喜欢自定义异常(而不是内置异常)吗?我们两个都回答一下吧。

自定义异常过度使用

您链接的 Dan Bader 的博客文章是一个很好的例子,说明了不应这样做。过度使用自定义异常的示例。每个异常类应该涵盖一组相关的用途(配置错误、浏览器错误、日期解析器错误)。您绝对不应该为需要引发某些情况的每种特定情况创建新的自定义异常。这就是异常消息的用途。

自定义异常与内置异常

这是一个更加基于意见的主题,它也很大程度上取决于特定的代码场景。我将展示两个有趣的示例(可能有很多示例),我认为在这些示例中使用自定义异常可能是有益的。

01:内部暴露

让我们创建一个简单的 Web 浏览器模块(围绕Requests https://pypi.org/project/requests/包裹):

import requests

def get(url):
    return requests.get(url)

现在假设您想要在包中的多个模块中使用新的 Web 浏览器模块。在其中一些中,您希望捕获一些可能与网络相关的异常:

import browser
import requests

try:
    browser.get(url)
except requests.RequestException:
    pass

该解决方案的缺点是您必须导入requests封装在每个模块中只是为了捕获异常。您还暴露了浏览器模块的内部结构。如果您决定将底层 HTTP 库从 Requests 更改为其他库,则必须修改捕获异常的所有模块。捕获一些一般异常的替代方法也是灰心 https://stackoverflow.com/questions/4990718/about-catching-any-exception.


如果您在 Web 浏览器模块中创建自定义异常:

import requests

class RequestException(requests.RequestException):
    pass

def get(url):
    try:
        return requests.get(url)
    except requests.RequestException:
        raise RequestException

那么您的所有模块现在都将避免上述缺点:

import browser

try:
    browser.get(url)
except browser.RequestException:
    pass

请注意,这也正是 Requests 包本身使用的方法 - 它定义了自己的方法RequestException类,因此您不必导入底层urllib打包到您的网络浏览器模块中只是为了捕获它引发的异常。

02:阴影错误

自定义异常不仅仅是为了让代码变得更漂亮。看看你的代码(稍作修改的版本),你会发现一些非常邪恶的东西:

def validate(name, value):
    if len(name) < int(value):
        raise ValueError(f"Name too short: {name}")

    return name

现在有人会使用您的代码,但他宁愿捕获它并提供默认名称,而不是在短名称的情况下传播您的异常:

name = 'Thomas Jefferson'

try:
    username = validate(name, '1O')
except ValueError:
    username = 'default user'

代码看起来不错,不是吗?现在看这个:如果你改变name变量实际上是任何字符串,username变量将始终被设置为'default user'。如果您定义并引发了自定义异常ValidationError,这不会发生。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

我什么时候应该声明自定义异常? 的相关文章

  • 使用Python开发Web应用程序

    我一直在用 python 做一些工作 但这都是针对独立应用程序的 我很想知道 python 的任何分支是否支持 Web 开发 有人还会建议一个好的教程或网站吗 我可以从中学习一些使用 python 进行 Web 开发的基础知识 既然大家都说
  • Spark的distinct()函数是否仅对每个分区中的不同元组进行洗牌

    据我了解 distinct 哈希分区 RDD 来识别唯一键 但它是否针对仅移动每个分区的不同元组进行了优化 想象一个具有以下分区的 RDD 1 2 2 1 4 2 2 1 3 3 5 4 5 5 5 在此 RDD 上的不同键上 所有重复键
  • Python tcl 未正确安装

    我刚刚为 python 安装了graphics py 但是当我尝试运行以下代码时 from graphics import def main win GraphWin My Circle 100 100 c Circle Point 50
  • __del__ 真的是析构函数吗?

    我主要用 C 做事情 其中 析构函数方法实际上是为了销毁所获取的资源 最近我开始使用python 这真的很有趣而且很棒 我开始了解到它有像java一样的GC 因此 没有过分强调对象所有权 构造和销毁 据我所知 init 方法对我来说在 py
  • IRichBolt 在storm-1.0.0 和 pyleus-0.3.0 上运行拓扑时出错

    我正在运行风暴拓扑 pyleus verbose local xyz topology jar using storm 1 0 0 pyleus 0 3 0 centos 6 6并得到错误 线程 main java lang NoClass
  • Python 中的二进制缓冲区

    在Python中你可以使用StringIO https docs python org library struct html用于字符数据的类似文件的缓冲区 内存映射文件 https docs python org library mmap
  • feedparser 在脚本运行期间失败,但无法在交互式 python 控制台中重现

    当我运行 eclipse 或在 iPython 中运行脚本时 它失败了 ascii codec can t decode byte 0xe2 in position 32 ordinal not in range 128 我不知道为什么 但
  • 在pyyaml中表示具有相同基类的不同类的实例

    我有一些单元测试集 希望将每个测试运行的结果存储为 YAML 文件以供进一步分析 YAML 格式的转储数据在几个方面满足我的需求 但测试属于不同的套装 结果有不同的父类 这是我所拥有的示例 gt gt gt rz shorthand for
  • python 集合可以包含的值的数量是否有限制?

    我正在尝试使用 python 设置作为 mysql 表中 ids 的过滤器 python集存储了所有要过滤的id 现在大约有30000个 这个数字会随着时间的推移慢慢增长 我担心python集的最大容量 它可以包含的元素数量有限制吗 您最大
  • 当玩家触摸屏幕一侧时,如何让 pygame 发出警告?

    我使用 pygame 创建了一个游戏 当玩家触摸屏幕一侧时 我想让 pygame 给出类似 你不能触摸屏幕两侧 的错误 我尝试在互联网上搜索 但没有找到任何好的结果 我想过在屏幕外添加一个方块 当玩家触摸该方块时 它会发出警告 但这花了很长
  • Geopandas 设置几何图形:MultiPolygon“等于 len 键和值”的 ValueError

    我有 2 个带有几何列的地理数据框 我将一些几何图形从 1 个复制到另一个 这对于多边形效果很好 但对于任何 有效 多多边形都会返回 ValueError 请指教如何解决这个问题 我不知道是否 如何 为什么应该更改 MultiPolygon
  • 循环中断打破tqdm

    下面的简单代码使用tqdm https github com tqdm tqdm在循环迭代时显示进度条 import tqdm for f in tqdm tqdm range 100000000 if f gt 100000000 4 b
  • 如何知道抛出了哪个异常

    我正在对我们的代码库进行审查 有很多这样的陈述 try doSomething catch Exception e 但我想要一种方法来知道 doSomething 抛出了哪个异常 在 doSomething 的实现中没有 throw 语句
  • Python 3 中“map”类型的对象没有 len()

    我在使用 Python 3 时遇到问题 我得到了 Python 2 7 代码 目前我正在尝试更新它 我收到错误 类型错误 map 类型的对象没有 len 在这部分 str len seed candidates 在我像这样初始化它之前 se
  • 从 pygame 获取 numpy 数组

    我想通过 python 访问我的网络摄像头 不幸的是 由于网络摄像头的原因 openCV 无法工作 Pygame camera 使用以下代码就像魅力一样 from pygame import camera display camera in
  • Nuitka 未使用 nuitka --recurse-all hello.py [错误] 编译 exe

    我正在尝试通过 nuitka 创建一个简单的 exe 这样我就可以在我的笔记本电脑上运行它 而无需安装 Python 我在 Windows 10 上并使用 Anaconda Python 3 我输入 nuitka recurse all h
  • 在Python中重置生成器对象

    我有一个由多个yield 返回的生成器对象 准备调用该生成器是相当耗时的操作 这就是为什么我想多次重复使用生成器 y FunctionWithYield for x in y print x here must be something t
  • 对输入求 Keras 模型的导数返回全零

    所以我有一个 Keras 模型 我想将模型的梯度应用于其输入 这就是我所做的 import tensorflow as tf from keras models import Sequential from keras layers imp
  • 在python中,如何仅搜索所选子字符串之前的一个单词

    给定文本文件中的长行列表 我只想返回紧邻其前面的子字符串 例如单词狗 描述狗的单词 例如 假设有这些行包含狗 hotdog big dog is dogged dog spy with my dog brown dogs 在这种情况下 期望
  • 循环标记时出现“ValueError:无法识别的标记样式 -d”

    我正在尝试编码pyplot允许不同标记样式的绘图 这些图是循环生成的 标记是从列表中选取的 为了演示目的 我还提供了一个颜色列表 版本是Python 2 7 9 IPython 3 0 0 matplotlib 1 4 3 这是一个简单的代

随机推荐