<p>有两个问题合二为一:<em>我应该多久使用一次自定义异常(以避免过度使用它们)?</em>和<em>我真的应该更喜欢自定义异常(而不是内置的)?让我们两个都回答。你知道吗</p>
<h2>自定义异常过度使用</h2>
<p>你链接的丹·贝德的博客文章是一个很好的例子,说明了不应该这样做。过度使用自定义异常的示例。每个异常类都应该包含一组相关的用法(ConfigError、browserror、dateparserror)。您绝对不应该为每一个需要提出问题的特定情况创建一个新的自定义异常。这就是异常消息的用途。你知道吗</p>
<h2>自定义与内置异常</h2>
<p>这是一个更基于观点的主题,而且它在很大程度上取决于特定的代码场景。我将展示两个有趣的示例(可能有很多),其中我认为使用自定义异常是有益的。你知道吗</p>
<h2>01:堆内构件暴露</h2>
<p>让我们创建一个简单的web浏览器模块(围绕<a href="https://pypi.org/project/requests/" rel="nofollow noreferrer">Requests</a>包的一个薄包装器):</p>
<pre class="lang-py prettyprint-override"><code>import requests
def get(url):
return requests.get(url)
</code></pre>
<p>现在,假设您希望在包中的多个模块中使用新的web浏览器模块。在其中一些情况下,您希望捕获一些可能与网络相关的异常:</p>
<pre class="lang-py prettyprint-override"><code>import browser
import requests
try:
browser.get(url)
except requests.RequestException:
pass
</code></pre>
<p>此解决方案的缺点是,您必须在每个模块中导入<code>requests</code>包,以便捕获异常。此外,您还公开了浏览器模块的内部结构。如果您决定将底层HTTP库从请求更改为其他内容,则必须修改捕获异常的所有模块。另一种捕获一般异常的方法是<a href="https://stackoverflow.com/questions/4990718/about-catching-any-exception">discouraged</a>。你知道吗</p>
<hr/>
<p>如果在web浏览器模块中创建自定义异常:</p>
<pre class="lang-py prettyprint-override"><code>import requests
class RequestException(requests.RequestException):
pass
def get(url):
try:
return requests.get(url)
except requests.RequestException:
raise RequestException
</code></pre>
<p>现在,所有模块都将避免上述缺点:</p>
<pre class="lang-py prettyprint-override"><code>import browser
try:
browser.get(url)
except browser.RequestException:
pass
</code></pre>
<p>请注意,这也正是Requests包本身中使用的方法—它定义了自己的<code>RequestException</code>类,这样您就不必在web浏览器模块中导入底层的<code>urllib</code>包来捕获它引发的异常。你知道吗</p>
<h2>02:错误阴影</h2>
<p>自定义异常不仅仅是为了让代码更漂亮。看看你的代码(稍加修改的版本),你会发现一些真正邪恶的东西:</p>
<pre class="lang-py prettyprint-override"><code>def validate(name, value):
if len(name) < int(value):
raise ValueError(f"Name too short: {name}")
return name
</code></pre>
<p>现在有人将使用您的代码,但他宁愿捕获并提供默认名称,而不是在短名称的情况下传播您的异常:</p>
<pre class="lang-py prettyprint-override"><code>name = 'Thomas Jefferson'
try:
username = validate(name, '1O')
except ValueError:
username = 'default user'
</code></pre>
<p>代码看起来不错,不是吗?现在请注意:如果将<code>name</code>变量更改为任意字符串,<code>username</code>变量将始终设置为<code>'default user'</code>。如果定义并引发自定义异常<code>ValidationError</code>,则不会发生这种情况。你知道吗</p>