如何在python中注册从抽象类继承的类
Posted
技术标签:
【中文标题】如何在python中注册从抽象类继承的类【英文标题】:How to register classes that inherit from an abstract class in python 【发布时间】:2021-07-04 04:56:12 【问题描述】:我正在创建一个具有作为已实现类的工厂的函数的模块。它在元类的帮助下注册类(我刚刚从here复制了一个模式)。
_registry =
def register_class(cls):
_registry[cls.__name__] = cls
class Meta(type):
def __new__(meta, name, bases, class_dict):
cls = type.__new__(meta, name, bases, class_dict)
register_class(cls)
return cls
def factory(name):
return _registry[name]()
到目前为止有效。
现在我的一个特殊性是我正在实现共享很多功能的类,因此我定义了一个实现大多数共享逻辑的抽象基类,以及大量改进某些特殊性的派生类。问题是这会导致元类冲突,因为派生类的元类既是ABCmeta
又是Meta
:
from abc import ABC, abstractmethod
_registry =
def register_class(cls):
_registry[cls.__name__] = cls
class Meta(type):
def __new__(meta, name, bases, class_dict):
cls = type.__new__(meta, name, bases, class_dict)
register_class(cls)
return cls
def factory(name):
return _registry[name]()
class Base(ABC):
pass
class Derived1(Base, metaclass=Meta):
pass
TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
我该如何解决这个冲突?
【问题讨论】:
我通常不使用这些东西,因为我不喜欢形式或觉得它有用。但是,我认为与其使用从ABC
继承的Base
,不如让Meta
从ABCMeta
继承(适当地使用super()
。或者,您是否考虑过只使用装饰器来做注册?显然它是每个类的额外行,但它取代了metaclass=
参数,我认为它可以说更明确。
【参考方案1】:
只需将您需要的元类组合成一个合适的派生元类,然后将其用作您的元类。在这种情况下,jsut 从“ABCMeta”而不是类型派生您的元类:
from abc import ABCMeta
class Meta(ABCMeta):
def __new__(meta, name, bases, class_dict):
cls = super().__new__(meta, name, bases, class_dict)
register_class(cls)
return cls
注意使用super().__new__
而不是type.__new__
的重要性——这是我们的元类能够与其他元类组合的必要条件(只要它们都不会直接干扰您自己的相同属性/逻辑元类正在开发中)。
因此,如果您需要 一些 类来使用 ABCMeta,而有些类会单独使用您的元类,您可以,只需将对 type.__new__
的调用替换为 super().__new__
使用您的元类作为 mixin,根据需要组合 ABCMeta:
from abc import ABCMeta
class Meta(type):
...
class MetaWithAbc(Meta, ABCMeta):
pass
class Base(metaclass=MetaWithAbc):
pass
...
此外,自 Python 3.6 以来,随着特殊 __init_subclass__
方法的引入,对元类的需求已大大减少。
只需向注册表添加一个类,在这种情况下,如果您有一个公共基类,则不需要自定义元类:__init_subclass__
在创建时每个子类调用一次:
from abc import ABC, abstractmethod
_registry =
def register_class(cls):
_registry[cls.__name__] = cls
def factory(name):
return _registry[name]()
class Base(ABC):
def __init_subclass__(cls, **kwargs):
# always make it colaborative:
super().__init_subclass__(cls, **kwargs)
register_class(cls)
class Derived1(Base):
pass
【讨论】:
以上是关于如何在python中注册从抽象类继承的类的主要内容,如果未能解决你的问题,请参考以下文章