Python 避免使用元类继承生成的类属性
我在考虑自动将子类添加到父类中,以便使用元类进行“链接”。然而,从父类继承这些属性会把事情搞砸。有没有好办法避免这种情况Python 避免使用元类继承生成的类属性,python,inheritance,metaclass,Python,Inheritance,Metaclass,我在考虑自动将子类添加到父类中,以便使用元类进行“链接”。然而,从父类继承这些属性会把事情搞砸。有没有好办法避免这种情况 class MetaError(type): def __init__(cls, name, bases, attrs): for base in bases: setattr(base, name, cls) super(MetaError, cls).__init__(name, bases, attrs)
class MetaError(type):
def __init__(cls, name, bases, attrs):
for base in bases:
setattr(base, name, cls)
super(MetaError, cls).__init__(name, bases, attrs)
class BaseError(Exception, object):
def __init__(self, message):
super(BaseError, self).__init__(message)
class HttpError(BaseError):
__metaclass__ = MetaError
class HttpBadRequest(HttpError):
pass
class HttpNotFound(HttpError):
pass
class FileNotFound(HttpNotFound):
pass
class InvalidJson(HttpBadRequest):
pass
http = HttpError
# now I can do
raise http.HttpNotFound('Not found')
raise http.HttpNotFound.FileNotFound('File not found')
raise http.HttpBadRequest.InvalidJson('Invalid json')
# unfortunately this also works
raise http.HttpBadRequest.HttpBadRequest('Bad request')
raise http.HttpBadRequest.HttpNotFound('Not found')
好吧,这比一开始看起来更棘手- 因为基本上您希望具有类继承关系,但不使用类继承上的常规属性查找路径- 否则,HTTPError作为BaseError的一个子类,总是在BaseError本身中存在所有属性-因此, 链
BaseError.HTTPError.HTTPError.HTTPError.HTTPError…
始终有效
幸运的是,Python确实提供了一种机制,可以将类注册为其他类的子类,而无需“物理”继承—也就是说,它被报告为子类,但其基类中没有父类或\uuuuuuuuuuuuuuu
—因此,派生类上的属性查找(采用?)不会在“foster”中搜索属性家长
此机制通过“”或“abc”提供,通过其ABCMeta元类和“register”方法提供
现在,由于这个事实,你可能还想申报
您的类层次结构具有正常的继承语法-即,
能够写入类HTTPError(BaseError):
以指示新的
类派生自BaseError-您将获得实际的“物理”继承
因此,我们可以从ABCMeta类继承(而不是type
)并编写
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuu新方法,以便排除物理继承-
我们还使用setattr
来包含您想要包含的代码,而且,我们还触发对parentclass的所需调用。直接在元类上注册
(请注意,由于我们现在正在更改基类,因此需要修改
在元类的\uuuuu new\uuuuu
方法中,而不是在\uuuuu init\uuuuu
上:
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'
注意:无需显式地从异常
和对象
继承-异常
本身已经从对象
继承。而且,最重要的是:无论您在做什么项目,尽可能将其移动到Python3.x而不是Python2。Python2已经计算了天数,并且Python 3中有很多新特性,您无法使用。(这个答案中的代码与Python 2/3兼容,但是对于\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
m = {}
class MetaError(type):
def __init__(cls, name, bases, attrs):
for base in bases:
m[(base, name)] = cls
super(MetaError, cls).__init__(name, bases, attrs)
def __getattribute__(self, value):
if (self, value) in m:
return m[self, value]
return type.__getattribute__(self, value)
class BaseError(Exception):
__metaclass__ = MetaError
class HttpError(BaseError):
pass
class HttpBadRequest(HttpError):
pass
class HttpNotFound(HttpError):
pass
class FileNotFound(HttpNotFound):
pass
class InvalidJson(HttpBadRequest):
pass
谢谢你的回答。我很可能是最好的选择(或者,基于全球地图的解决方案也会起作用-作为答案发布)
m = {}
class MetaError(type):
def __init__(cls, name, bases, attrs):
for base in bases:
m[(base, name)] = cls
super(MetaError, cls).__init__(name, bases, attrs)
def __getattribute__(self, value):
if (self, value) in m:
return m[self, value]
return type.__getattribute__(self, value)
class BaseError(Exception):
__metaclass__ = MetaError
class HttpError(BaseError):
pass
class HttpBadRequest(HttpError):
pass
class HttpNotFound(HttpError):
pass
class FileNotFound(HttpNotFound):
pass
class InvalidJson(HttpBadRequest):
pass