Ket*_*uem 5 python metaprogramming flask flask-restful
我正在尝试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'\1-\2', cls.__name__)
return '/' + re.sub('([a-z0-9])([A-Z])', r'\1-\2', s).lower()
Run Code Online (Sandbox Code Playgroud)
当整个事情发布时,我得到以下内容:
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
Run Code Online (Sandbox Code Playgroud)
我已尝试使用代理类层,但结果仍然相同.那么有没有办法使用这种模式注册我的资源?
小智 11
元类是用于构建类的类,这意味着类的结构 - __dict__例如,它的内部或由__getattribute__- 提供的自动委托机制- 由元类创建到类中.
当您从父类继承时,您隐式继承其元类,因为您基本上只是扩展父类的结构,而父类的结构部分由其元类提供.
因此,当您从一个类继承时,您要么保留父类给出的元类,要么定义一个从父类派生的元类.在多重继承的情况下也是如此.
错误的Python代码的一个例子是
class ParentType(type):
pass
class Parent(metaclass=ParentType):
pass
class ChildType(type):
pass
class Child(Parent, metaclass=ChildType):
pass
Run Code Online (Sandbox Code Playgroud)
这完全给出了您在问题中提到的错误.制作ChildType子类ParentType可以解决问题
class ParentType(type):
pass
class Parent(metaclass=ParentType):
pass
class ChildType(ParentType):
pass
class Child(Parent, metaclass=ChildType):
pass
Run Code Online (Sandbox Code Playgroud)
Dologan给出的答案正确地解决了创建元类的问题,该元类继承自两个元类,然后直接使用它.
但是,如果可能的话,我会避免这种复杂的解决方案.多重继承有点难以管理 - 不是坏事,只是复杂 - 而在元类中执行它可能会导致难以掌握的事情.
这显然取决于您要完成的工作以及您记录和测试解决方案的好处.如果您想要或需要使用元类来解决它,我建议使用抽象基类(基本上是类别).ABC在其_abc_registry属性中包含已注册类的列表,但没有官方方法可以获取它,最好避免直接使用它.但是,您可以派生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
Run Code Online (Sandbox Code Playgroud)
您现在可以创建仅继承RegistryABC和使用该register()方法的类别:
class MyCategory(RegistryABC):
pass
MyCategory.register(tuple)
MyCategory.register(str)
print(MyCategory.registry)
Run Code Online (Sandbox Code Playgroud)
你得到的是[<class 'tuple'>, <class 'str'>].
您的DefaultResource类似乎继承自具有两个不同元类的类:(BaseRegistered使用 metaclass ResourceRegistry)和Resource(使用 Flask 的MethodViewType元类)。
这个答案会建议做类似的事情:
from flask.views import MethodViewType
class CombinedType(ResourceRegistry, MethodViewType):
pass
class BaseRegistered(object):
__metaclass__ = Combinedtype
Run Code Online (Sandbox Code Playgroud)
然后像以前一样继续。