我应该何时声明自定义例外?

2024-10-03 06:29:49 发布

您现在位置:Python中文网/ 问答频道 /正文

我想引发一些异常,这些异常传递一些消息和与错误相关的值。我想知道什么时候声明自定义异常比使用内置异常更合适。你知道吗

我见过很多类似this的例子,还有很多类似的例子被推荐到其他网站上。你知道吗

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}")

我的直觉是,只有当复杂或特定的信息需要存储在异常实例中时,才会声明自定义异常。声明空类在我看来是错误的。你知道吗


Tags: name声明消息lenif网站def错误
3条回答

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

自定义异常过度使用

你链接的丹·贝德的博客文章是一个很好的例子,说明了不应该这样做。过度使用自定义异常的示例。每个异常类都应该包含一组相关的用法(ConfigError、browserror、dateparserror)。您绝对不应该为每一个需要提出问题的特定情况创建一个新的自定义异常。这就是异常消息的用途。你知道吗

自定义与内置异常

这是一个更基于观点的主题,而且它在很大程度上取决于特定的代码场景。我将展示两个有趣的示例(可能有很多),其中我认为使用自定义异常是有益的。你知道吗

01:堆内构件暴露

让我们创建一个简单的web浏览器模块(围绕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库从请求更改为其他内容,则必须修改捕获异常的所有模块。另一种捕获一般异常的方法是discouraged。你知道吗


如果在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类,这样您就不必在web浏览器模块中导入底层的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,则不会发生这种情况。你知道吗

正在创建自定义异常类

  • 提供程序可能产生的所有预期错误的声明性清单;使维护更容易

  • 允许您有选择地捕获特定的异常,特别是在您建立有用的异常层次结构时:

    class ValidationError(ValueError):
        pass
    
    class NameTooShortError(ValidationError):
        pass
    
    ...
    
    class DatabaseError(RuntimeError):
        pass
    
    class DatabaseWriteError(DatabaseError):
        pass
    
  • 允许您更好地将表示和代码分开:您放入异常的消息不一定是最终用户将看到的消息,特别是如果您将应用程序本地化为多种语言。对于自定义类,您可以这样编写前端(使用通用的HTML模板语法,_()是gettext本地化函数):

    {% if isinstance(e, NameTooShortError) %}
      <p>{{ _('Entered name is too short, enter at least %d characters') % e.min_length }}</p>
    {% elif isinstance(...) %}
      ...
    {% else %}
      {# fallback for unexpected exceptions #}
      <p>{{ _('An error occurred: %s') % e }}</p>
    {% endif %}
    

    试着用ValueError(f'Name too short: {name}')

本, 决定何时声明自定义异常是一个私人问题。就我个人而言,当我遇到错误时,我喜欢使用它们,我盯着屏幕搔首弄姿,想知道它到底意味着什么,或者是在一些宽泛的情况下。在您给出的示例中,我个人会将其设置为“Name too short:{Name}请输入一个大于10个字符的名称”,这样可能不确切知道所需长度的最终用户就能够理解为什么会出现这样的错误。 希望这有帮助:)

相关问题 更多 >