0

I'm trying to dynamically inherit from a class MyMixin with metaclasses. Using this excelent answer and this code snippet, I got this working:

class DynamicInheritance(type):
    """
    Dinamicaly modify class with some extra mixin.
    """
    def __call__(cls, mixin, *args, **kwargs):
        new_cls = type(cls.__name__, (mixin, cls), dict(cls.__dict__))
        return super(DynamicInheritance, new_cls).__call__(*args, **kwargs)

class MyMixin:
    def bye(self):
        print("bye")

class Foo(metaclass=DynamicInheritance):
    def hello(self):
        print("hello")

foo_instance = Foo(MyMixin)
foo_instance.hello()
foo_instance.bye()

Output:

hello
bye

However, when Foo inherits from an abstract class, things stop working:

from abc import ABC, abstractmethod

class FooAbstract(ABC):
    @abstractmethod
    def hello(self):
        pass

    @abstractmethod
    def bye(self):
        pass

class Foo(FooAbstract, metaclass=DynamicInheritance):
    def hello(self):
        print("hello")

foo_instance = Foo(MyMixin)

Output:

TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases

Can someone explain what the problem is?

skd
  • 1,865
  • 1
  • 21
  • 29
  • If I understand correctly, calling `Foo` both creates a new subclass of `Foo` on the fly *and* instantiates it. I wouldn't try to combine those two. What's wrong with `foo_instance = type('...', (Foo, MyMixin), {})()` instead, dispensing with the metaclass altogether? (Assuming there's a reason to create such one-time-only classes in the first place. Given `f1 = Foo(MyMixin)` and `f2 = Foo(MyMixin)`, `f1` and `f2` do *not* have the same type.) – chepner Jul 06 '22 at 14:18
  • I tried to simplify my example as much as possible. In the real use case, there are two "MyMixin1" and "MyMixin2" and we want to inherit from one or another base on some condition. – skd Jul 06 '22 at 14:20
  • The problem is that metaclasses do not easily compose. `ABCMeta` and `DynamicInheritance` are both subclasses of `type`, but neither is a subclass of the other. Python will not even let you *try* to mix the two together unless you can demonstrate via subclassing that one is a kind of the other. – chepner Jul 06 '22 at 14:21
  • By "combine", I mean defining some class like `class DIABC(ABCMeta, DynamicInheritance): ...` that explicitly spells out how to make use of both in a sensible way. – chepner Jul 06 '22 at 14:23
  • Closing as a duplicate. This question arises with certain frequency, and there are better answers if you search for "metaclass conflict".. I just happened to link to one I answered last week or so. – jsbueno Jul 06 '22 at 21:22
  • It is actually very difficult to arrive to your answer (which is great) when searching for this type of problem. – skd Jul 07 '22 at 10:39

0 Answers0