python - 我应该何时声明自定义异常?
问题描述
我想引发传达一些消息和与错误相关的值的异常。我想知道何时最适合声明自定义异常而不是使用内置异常。
我见过很多这样的例子,还有更多类似的例子在其他网站上被推荐。
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}")
我的直觉是仅在需要将复杂或特定信息存储在异常实例中时才声明自定义异常。声明空班对我来说似乎是错误的。
解决方案
有两个问题合并为一个:我应该多久使用一次自定义异常(不要过度使用它们)?并且我真的应该更喜欢自定义异常(而不是内置异常)吗?让我们两个都回答。
自定义异常过度使用
您链接的 Dan Bader 的博客文章是一个很好的例子,说明不应该这样做。过度使用自定义异常的示例。每个异常类都应涵盖一组相关用途(ConfigError、BrowserError、DateParserError)。您绝对不应该为需要提出某些问题的每个特定情况创建新的自定义异常。这就是异常消息的用途。
自定义与内置异常
这是一个更基于意见的主题,它也高度依赖于特定的代码场景。我将展示两个有趣的例子(可能很多),我认为使用自定义异常可能是有益的。
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 库从 Requests 更改为其他内容,则必须修改捕获异常的所有模块。捕获一些一般异常的替代方法也是不鼓励的。
如果您在 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
为了捕获它引发的异常而在 Web 浏览器模块中导入底层包。
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
,则不会发生这种情况。
推荐阅读
- c - 活动数据位置和活动展示位置之间的区别
- android - 自定义视图的 OnClick 方法从未调用过 (Android)
- azure - Azure DB 同步:将多个中心成员对与单个共享同步数据库同步
- android - 为什么地理定位在带有 React Native 的 Android 模拟器上不起作用?
- servlets - 如何避免并发请求使用相同的 servlet 全局变量
- reactjs - 状态更改后组件不会重新渲染
- c# - X509Certificate2.FriendlyName 属性不检索 Linux 上的证书标签
- svn - JIRA 中特定问题的 SVN 提交消息
- vba - 自动运行 VBA 以自动刷新所有数据源
- javascript - 使用 navigator.mediaDevices.getUserMedia 时出现 TypeError