簡體   English   中英

如何在 python 中注冊從抽象 class 繼承的類

[英]How to register classes that inherit from an abstract class in python

我正在創建一個帶有 function 的模塊,該模塊用作已實現類的工廠。 它在元類(我剛剛從這里復制的模式)的幫助下注冊類。

  _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 來實現大部分共享邏輯,以及大量改進某些特殊性的派生類。 問題是這會導致元類沖突,因為派生類的元類既是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

...

此外,自 Python 3.6 以來,隨着特殊__init_subclass__方法的引入,對元類的需求已大大減少。 簡單地將 class 添加到注冊表中,在這種情況下,如果您有一個公共基類,則不需要自定義元類:每個子類調用一次__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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM