如何在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,不如让MetaABCMeta 继承(适当地使用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中注册从抽象类继承的类的主要内容,如果未能解决你的问题,请参考以下文章

Django:从带有元的抽象类的多重继承

Python 接口:从协议到抽象基类

抽象类,派生类,继承

您可以强制从抽象基类继承的类仅具有在基本情况下定义的公共方法吗?

Python基础----继承派生组合接口和抽象类

12.面向对象(继承/super/接口/抽象类)