<p>好吧,这比一开始看起来要复杂-
因为基本上你想要有类继承关系,但是不要在类继承上使用普通的属性查找路径-
否则,HTTPError作为BaseError的一个子类,将始终拥有BaseError本身的所有属性,因此,
链<code>BaseError.HTTPError.HTTPError.HTTPError.HTTPError...</code>将始终有效。在</p>
<p>幸运的是,Python提供了一种机制,可以将类注册为其他类的子类,而不需要“物理”继承——也就是说,它被报告为子类,但它的基类或<code>__mro__</code>中没有父类,因此,在派生类上进行属性查找(采用了吗?)不在“寄养”父级中搜索属性。在</p>
<p>这个机制是通过“<a href="https://docs.python.org/3/library/abc.html" rel="nofollow noreferrer">abstract base classes</a>”或“abc”通过其ABCMeta元类和“register”方法提供的。在</p>
<p>现在,由于你可能还想申报
使用普通继承语法的类层次结构-即,
能够写<code>class HTTPError(BaseError):</code>来表示新的
类派生自BaseError-您可以获得实际的“物理”继承。在</p>
<p>因此,我们可以从ABCMeta类继承(而不是<code>type</code>)并编写
使物理继承被排除的<code>__new__</code>方法-
我们还使用<code>setattr</code>来包含您的代码,而且,我们直接在元类上触发对<code>parentclass.register</code>的必要调用。在</p>
<p>(请注意,由于我们现在正在更改基类,我们需要进行调整
在元类的<code>__new__</code>方法中,而不是在<code>__init__</code>上:</p>
<pre><code>from abc import ABCMeta
class MetaError(ABCMeta):
def __new__(metacls, name, bases, attrs):
new_bases = []
base_iter = list(reversed(bases))
seen = []
register_this = None
while base_iter:
base = base_iter.pop(0)
if base in seen:
continue
seen.append(base)
if isinstance(base, MetaError):
register_this = base
base_iter = list(reversed(base.__mro__)) + base_iter
else:
new_bases.insert(0, base)
cls = super(MetaError, metacls).__new__(metacls, name, tuple(new_bases), attrs)
if register_this:
setattr(register_this, name, cls)
register_this.register(cls)
return cls
</code></pre>
<p>快速测试:</p>
^{pr2}$
<p>在交互模式下,检查它是否按预期工作:</p>
<pre><code>In [38]: BaseError.HTTPError
Out[38]: __main__.HTTPError
In [39]: BaseError.HTTPError.HTTPError
-
AttributeError Traceback (most recent call last)
<ipython-input-39-5d5d03751646> in <module>()
> 1 BaseError.HTTPError.HTTPError
AttributeError: type object 'HTTPError' has no attribute 'HTTPError'
In [40]: HTTPError.__mro__
Out[40]: (__main__.HTTPError, Exception, BaseException, object)
In [41]: issubclass(HTTPError, BaseError)
Out[41]: True
In [42]: issubclass(HTTPBadRequest, BaseError)
Out[42]: True
In [43]: BaseError.HTTPError.HTTPBadRequest
Out[43]: __main__.HTTPBadRequest
In [44]: BaseError.HTTPBadRequest
-
AttributeError Traceback (most recent call last)
<ipython-input-44-b40d65ca66c6> in <module>()
> 1 BaseError.HTTPBadRequest
AttributeError: type object 'BaseError' has no attribute 'HTTPBadRequest'
</code></pre>
<p>然后,最重要的是,测试异常层次结构是否真的以这种方式工作:</p>
<pre><code>In [45]: try:
....: raise HTTPError
....: except BaseError:
....: print("it works")
....: except HTTPError:
....: print("not so much")
....:
it works
</code></pre>
<p>注意:不需要同时从<code>Exception</code>和<code>object</code>显式继承-<code>Exception</code>本身已经从<code>object</code>继承。而且,最重要的是:无论你在做什么项目,尽一切可能把它移到python3.x而不是python2上。Python2是有天数的,而且Python3中有很多很多新特性,您不需要自己使用。(这个答案中的代码是python2/3兼容的,当然是针对<code>__metaclass__</code>用法声明)。在</p>