2

我已经看到允许在运行时添加基类的问题,但我有一个更具体的问题,试图通过使用元类在更深的层次结构中添加基类。

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'>)

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

4

1 回答 1

2

您正在抛弃当前的类层次结构,因为您完全从不同的基类构造了一个新类。如果Special要混入,则将其添加到当前类层次结构中。继承自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层次结构中的其他类之前。Special如果您需要在之后SpecificConcrete(但在之前)交换基类顺序Base

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

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

于 2017-08-24T16:34:35.470 回答