如何对元类使用多重继承?
How can I use multiple inheritance with a metaclass?
我正在尝试使用注册表模式注册我在 Flask-RESTFUL
中定义的所有资源。
from flask_restful import Resource
class ResourceRegistry(type):
REGISTRY = {}
def __new__(cls, name, bases, attrs):
new_cls = type.__new__(cls, name, bases, attrs)
cls.REGISTRY[new_cls.__name__] = new_cls
return new_cls
@classmethod
def get_registry(cls):
return dict(cls.REGISTRY)
class BaseRegistered(object):
__metaclass__ = ResourceRegistry
class DefaultResource(BaseRegistered, Resource):
@classmethod
def get_resource_name(cls):
s = re.sub('(.)([A-Z][a-z]+)', r'-', cls.__name__)
return '/' + re.sub('([a-z0-9])([A-Z])', r'-', s).lower()
当整个事情启动时,我得到以下信息:
TypeError: Error when calling the metaclass bases
metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
我试过代理层 类 但结果还是一样。那么有没有办法使用这种模式注册我的资源?
您的 DefaultResource
class 似乎继承自 classes 具有两个不同的元classes:BaseRegistered
(元class ResourceRegistry
) 和 Resource
(使用 Flask 的 MethodViewType
metaclass)。
This answer 建议执行以下操作:
from flask.views import MethodViewType
class CombinedType(ResourceRegistry, MethodViewType):
pass
class BaseRegistered(object):
__metaclass__ = Combinedtype
然后像以前一样进行。
元class是用来构建class的class,这意味着class的结构本身——它的内部__dict__
,例如,或 __getattribute__
提供的自动委托机制 - 由 metaclass.
创建到 class
当您从 parent class 继承时,您隐式继承了它的元 class,因为您基本上只是扩展了 parent [=43] 的结构=],部分由其 metaclass.
提供
因此,当您从 class 继承时,您必须保留 parent 给您的元数据 class 或定义一个新的元数据 class源自您的 parent。在多重继承的情况下也是如此。
错误的 Python 代码示例是
class ParentType(type):
pass
class Parent(metaclass=ParentType):
pass
class ChildType(type):
pass
class Child(Parent, metaclass=ChildType):
pass
这正是您在问题中提到的错误。使 ChildType
成为 ParentType
的子class 解决了问题
class ParentType(type):
pass
class Parent(metaclass=ParentType):
pass
class ChildType(ParentType):
pass
class Child(Parent, metaclass=ChildType):
pass
Dologan 给出的答案正确地解决了创建一个从两个 metaclasses 继承的 metaclass 然后直接使用它的问题。
但是,如果可能的话,我会避免这样复杂的解决方案。多重继承有点难以管理——不是坏事,只是复杂——在元class中进行多重继承确实会导致难以掌握。
这显然取决于您要完成的工作以及您记录和测试解决方案的程度。如果你想要或需要用 metaclasses 来解决它,我建议你利用 Abstract Base 类(这基本上是类别)。 ABCs在其_abc_registry
属性中包含了注册的classes的列表,但官方没有获取方法,最好避免直接使用。但是,您可以派生 ABCMeta 并使用此代码
使其保留 public 注册表
import abc
class RegistryABCMeta(abc.ABCMeta):
def __init__(self, name, bases, namespace):
super().__init__(name, bases, namespace)
self.registry = []
def register(self, subclass):
super().register(subclass)
self.registry.append(subclass)
class RegistryABC(metaclass=RegistryABCMeta):
pass
您现在可以创建仅继承自 RegistryABC
并使用 register()
方法的类别:
class MyCategory(RegistryABC):
pass
MyCategory.register(tuple)
MyCategory.register(str)
print(MyCategory.registry)
你得到的是[<class 'tuple'>, <class 'str'>]
。
它不需要很复杂,但是您必须决定一些 ResourceMixins 是否也应该在 REGISTRY 中注册(在这里他们会)。在 python 2 中只使用 __metaclass__ 我花了几个小时因为我没有保存代码并且想知道这是什么魔法。
在我的例子中,我想添加额外的 method_decorators,但是资源 class 已经声明了它,所以不可能在 attrs 中添加它,而是在 new_cls 对象上设置。也不要忘记调用 MethodViewType.__new__(super(),而不是类型)。
from flask_restful import Resource
from flask.views import MethodViewType
class ResourceRegistry(MethodViewType):
REGISTRY = {}
def __new__(cls, name, bases, attrs):
new_cls = super().__new__(cls, name, bases, attrs)
cls.REGISTRY[new_cls.__name__] = new_cls
return new_cls
@classmethod
def get_registry(cls):
return dict(cls.REGISTRY)
class DefaultResource(Resource, metaclass=ResourceRegistry):
@classmethod
def get_resource_name(cls):
s = re.sub('(.)([A-Z][a-z]+)', r'-', cls.__name__)
return '/' + re.sub('([a-z0-9])([A-Z])', r'-', s).lower()
我正在尝试使用注册表模式注册我在 Flask-RESTFUL
中定义的所有资源。
from flask_restful import Resource
class ResourceRegistry(type):
REGISTRY = {}
def __new__(cls, name, bases, attrs):
new_cls = type.__new__(cls, name, bases, attrs)
cls.REGISTRY[new_cls.__name__] = new_cls
return new_cls
@classmethod
def get_registry(cls):
return dict(cls.REGISTRY)
class BaseRegistered(object):
__metaclass__ = ResourceRegistry
class DefaultResource(BaseRegistered, Resource):
@classmethod
def get_resource_name(cls):
s = re.sub('(.)([A-Z][a-z]+)', r'-', cls.__name__)
return '/' + re.sub('([a-z0-9])([A-Z])', r'-', s).lower()
当整个事情启动时,我得到以下信息:
TypeError: Error when calling the metaclass bases
metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
我试过代理层 类 但结果还是一样。那么有没有办法使用这种模式注册我的资源?
您的 DefaultResource
class 似乎继承自 classes 具有两个不同的元classes:BaseRegistered
(元class ResourceRegistry
) 和 Resource
(使用 Flask 的 MethodViewType
metaclass)。
This answer 建议执行以下操作:
from flask.views import MethodViewType
class CombinedType(ResourceRegistry, MethodViewType):
pass
class BaseRegistered(object):
__metaclass__ = Combinedtype
然后像以前一样进行。
元class是用来构建class的class,这意味着class的结构本身——它的内部__dict__
,例如,或 __getattribute__
提供的自动委托机制 - 由 metaclass.
当您从 parent class 继承时,您隐式继承了它的元 class,因为您基本上只是扩展了 parent [=43] 的结构=],部分由其 metaclass.
提供因此,当您从 class 继承时,您必须保留 parent 给您的元数据 class 或定义一个新的元数据 class源自您的 parent。在多重继承的情况下也是如此。
错误的 Python 代码示例是
class ParentType(type):
pass
class Parent(metaclass=ParentType):
pass
class ChildType(type):
pass
class Child(Parent, metaclass=ChildType):
pass
这正是您在问题中提到的错误。使 ChildType
成为 ParentType
的子class 解决了问题
class ParentType(type):
pass
class Parent(metaclass=ParentType):
pass
class ChildType(ParentType):
pass
class Child(Parent, metaclass=ChildType):
pass
Dologan 给出的答案正确地解决了创建一个从两个 metaclasses 继承的 metaclass 然后直接使用它的问题。
但是,如果可能的话,我会避免这样复杂的解决方案。多重继承有点难以管理——不是坏事,只是复杂——在元class中进行多重继承确实会导致难以掌握。
这显然取决于您要完成的工作以及您记录和测试解决方案的程度。如果你想要或需要用 metaclasses 来解决它,我建议你利用 Abstract Base 类(这基本上是类别)。 ABCs在其_abc_registry
属性中包含了注册的classes的列表,但官方没有获取方法,最好避免直接使用。但是,您可以派生 ABCMeta 并使用此代码
import abc
class RegistryABCMeta(abc.ABCMeta):
def __init__(self, name, bases, namespace):
super().__init__(name, bases, namespace)
self.registry = []
def register(self, subclass):
super().register(subclass)
self.registry.append(subclass)
class RegistryABC(metaclass=RegistryABCMeta):
pass
您现在可以创建仅继承自 RegistryABC
并使用 register()
方法的类别:
class MyCategory(RegistryABC):
pass
MyCategory.register(tuple)
MyCategory.register(str)
print(MyCategory.registry)
你得到的是[<class 'tuple'>, <class 'str'>]
。
它不需要很复杂,但是您必须决定一些 ResourceMixins 是否也应该在 REGISTRY 中注册(在这里他们会)。在 python 2 中只使用 __metaclass__ 我花了几个小时因为我没有保存代码并且想知道这是什么魔法。 在我的例子中,我想添加额外的 method_decorators,但是资源 class 已经声明了它,所以不可能在 attrs 中添加它,而是在 new_cls 对象上设置。也不要忘记调用 MethodViewType.__new__(super(),而不是类型)。
from flask_restful import Resource
from flask.views import MethodViewType
class ResourceRegistry(MethodViewType):
REGISTRY = {}
def __new__(cls, name, bases, attrs):
new_cls = super().__new__(cls, name, bases, attrs)
cls.REGISTRY[new_cls.__name__] = new_cls
return new_cls
@classmethod
def get_registry(cls):
return dict(cls.REGISTRY)
class DefaultResource(Resource, metaclass=ResourceRegistry):
@classmethod
def get_resource_name(cls):
s = re.sub('(.)([A-Z][a-z]+)', r'-', cls.__name__)
return '/' + re.sub('([a-z0-9])([A-Z])', r'-', s).lower()