好了,这真可谓是麻烦比它作用似乎在第一 - 因为基本上你想拥有类的继承关系,但不要使用类继承通常属性查找路径 - 否则,HTTPError,例如,作为BaseError的子类,将始终使所有属性存在于BaseError本身中 - 因此, 链BaseError.HTTPError.HTTPError.HTTPError.HTTPError...
将始终有效。
幸运的是,Python做提供注册类别为其他子类的机制,没有“物理”的继承 - 也就是说,它被报告为子类,但没有在它的基地或__mro__
父类 - 和因此,派生类的属性查找(采用?)不会搜索“福斯特”父类中的属性。
该机制通过“abstract base classes”或“abc”,通过其ABCMeta元类和“注册”方法提供。
而现在,由于你也可能要声明 你的类层次结构与正常继承语法的事实 - 那就是, 能够编写class HTTPError(BaseError):
指示新 类从BaseError派生 - 你的实际“物理”继承。
因此,我们可以从ABCMeta类(而不是type
)继承和使物理继承排除写 的__new__
方法 - 和我们使用的setattr
你打算用你的代码为好,同时遏制,我们直接在元类上触发所需的parentclass.register
调用。
(注意,我们正在改变的基类,我们需要在元类的__new__
方法摆弄 ,而不是__init__
:
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
而对于一个快速测试:
class BaseError(Exception):
__metaclass__ = MetaError
class HTTPError(BaseError):
pass
class HTTPBadRequest(HTTPError):
pass
在交互模式下,检查是否正常工作,你打算:
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'
然后,最重要的是,测试如果异常层次结构以这种方式确实可以工作:
In [45]: try:
....: raise HTTPError
....: except BaseError:
....: print("it works")
....: except HTTPError:
....: print("not so much")
....:
it works
的几个注意事项:无需来自Exception
和object
明确继承 - Exception
本身已经从object
继承。最重要的是:无论你正在做什么项目,尽可能将它移动到Python 3.x而不是Python 2. Python 2与时间有关,Python 3中有很多很多新功能排除你自己的使用。 (这个答案中的代码是Python 2/3兼容的,但是对于__metaclass__
用法声明当然)。
感谢您的回答。我可能是最好的选择(或者,似乎基于全球地图的解决方案也可以工作 - 作为答案发布) – root