python - 如何在python中注册从抽象类继承的类
问题描述
我正在创建一个具有作为已实现类的工厂工作的函数的模块。它在元类(我刚刚从这里复制的模式)的帮助下注册类。
_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
我该如何解决这个冲突?
解决方案
只需将您需要的元类组合成一个合适的派生元类,然后将其用作您的元类。在这种情况下,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
...
__init_subclass__
此外,自 Python 3.6 以来,随着特殊方法的引入,对元类的需求已大大减少。要简单地将类添加到注册表,在这种情况下,如果您有一个公共基类,则不需要自定义元类:__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
推荐阅读
- java - 如何使用 JTextField、JButton 和 JLabel 构建带有图形区域paintComponent 的 JFrame 窗口?
- java - 我可以知道如何将uri转换为位图吗?
- c# - 检查字典中是否已经存在相同的对象
- php - woocommerce 自定义支付网关通过客户挂钩取消订单
- python-3.x - 升级到更强大的新笔记本电脑后,pymc3 运行非常缓慢
- bash - 尝试在 mac 上的 bash 脚本中运行 chrome 时出现问题
- r - 在分组数据中,如何仅按列的值排列 *some* 列的行,同时保持其余列不变?
- javascript - 为什么我尝试连接时无法访问 http 服务器
- c# - 运行读取时带有 IronOcr 的 PDF Ocr 抛出 SEHException
- javascript - 是否可以操作 Array.prototype.map() 的索引参数?