首页 > 解决方案 > 如何在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]()

到目前为止,这有效。

现在我的一个特殊性是我正在实现共享很多功能的类,因此我定义了一个实现大多数共享逻辑的抽象基类,以及大量改进某些特殊性的派生类。问题是这会导致元类冲突,因为派生类的元类既是ABCmetaMeta

  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
            

我该如何解决这个冲突?

标签: pythonfactory-patternmetaclass

解决方案


只需将您需要的元类组合成一个合适的派生元类,然后将其用作您的元类。在这种情况下,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

推荐阅读