Python 在运行时将 class 添加到 __mro__

Python add class to __mro__ at runtime

我看到了允许在运行时添加基础 class 的问题,但我有一个更具体的问题试图通过使用 meta[= 在更深的层次结构中添加基础 class 25=].

class Meta(type):
    def __call__(cls, *args, special=False, **kwargs):
        if special:
            cls = cls.__class__(cls.__name__ + Special.__name__, (Special,), {})
        return super(Meta, cls).__call__(*args, **kwargs)    

class Base(metaclass=Meta):
    def __init__(self, *args, **kwargs):
      super().__init__(*args, **kwargs)
      print('construct Base')    

class Special(Base):
    def __init__(self, *args, **kwargs):
      super().__init__(*args, **kwargs)
      print('construct Special')    

class Concrete(Base):
    def __init__(self, *args, **kwargs):
      super().__init__(*args, **kwargs)
      print('construct Concrete')    

class Specific(Concrete):
    def __init__(self, *args, **kwargs):
      super().__init__(*args, **kwargs)
      print('construct Specific')    

c = Specific(special=True)
print(c.__class__.__mro__) # (Specific, Special, Base, Object)

# construct Base
# construct Special
# (<class 'SpecificSpecial'>, <class 'Special'>, <class 'Base'>, <class 'object'>)

我希望能够在运行时在 metaclass __call__ 函数中将 Special class 添加到我的层次结构中。但这将覆盖 Concrete 基础 class,基本上将其删除。我不确定如何正确插入此底座。此外,在尝试创建新的 class 时,我丢失了 Specific 的构造函数。我真的 想在 __mro__ 中插入一个 class 而没有副作用。可能吗?

您正在抛弃当前的 class 层次结构,因为您正在从完全不同的基础 class 构建一个新的 class。如果 Special 是一个混入,添加 它到当前的 class 层级。从 Specialcls 继承:

class Meta(type):
    def __call__(cls, *args, special=False, **kwargs):
        if special:
            cls = cls.__class__(cls.__name__ + Special.__name__, (Special, cls), {})
        return super(Meta, cls).__call__(*args, **kwargs)

然后输出变成:

>>> c = Specific(special=True)
construct Base
construct Concrete
construct Specific
construct Special
>>> print(c.__class__.__mro__)
(<class '__main__.SpecificSpecial'>, <class '__main__.Special'>, <class '__main__.Specific'>, <class '__main__.Concrete'>, <class '__main__.Base'>, <class 'object'>)

这会将 Special 置于层次结构中其他 class 之前。如果您需要 SpecialSpecificConcrete 之后(但在 Base 之前),请交换基础 class 顺序:

cls = cls.__class__(cls.__name__ + Special.__name__, (cls, Special), {})

请注意,这会在您每次创建实例时创建一个新的 class 。您可能希望缓存 classes 以至少避免 class 对象的扩散。