A metaclass is the class used to build a class, which means that the very structure of a class - its internal __dict__
, for example, or the automatic delegation mechanism provided by __getattribute__
- are created into the class by the metaclass.
When you inherit from a parent class you are implicitly inheriting its metaclass, because you are basically just extending the structure of the parent class, which is in part provided by its metaclass.
So, when you inherit from a class you have either to keep the metaclass given you by your parent or define a new metaclass which is derived from that of your parent. This is true in the case of multiple inheritance as well.
An example of wrong Python code is
class ParentType(type):
pass
class Parent(metaclass=ParentType):
pass
class ChildType(type):
pass
class Child(Parent, metaclass=ChildType):
pass
This gives exactly the error you mention in your question. Making ChildType
a subclass of ParentType
solves the issue
class ParentType(type):
pass
class Parent(metaclass=ParentType):
pass
class ChildType(ParentType):
pass
class Child(Parent, metaclass=ChildType):
pass
The answer given by Dologan correctly addresses the problem creating a metaclass which inherits from both the metaclasses and then use it straightforwardly.
I would, if possible, avoid such a complex solution, however. Multiple inheritance is a bit difficult to manage - not a bad thing, just complex - and doing it in a metaclass could really result in something difficult to grasp.
It obviously depends on what you are trying to accomplish and how good you document and test the solution. If you want or need to solve it with metaclasses I would suggest to leverage the Abstract Base Classes (which are basically categories). ABCs contain the list of the registered classes in their _abc_registry
attribute, but there is no official method to get it, and it is better to avoid directly using it. You may however derive ABCMeta and make it keep a public registry with this code
import abc
class RegistryABCMeta(abc.ABCMeta):
def __init__(self, name, bases, namespace):
super().__init__(name, bases, namespace)
self.registry = []
def register(self, subclass):
super().register(subclass)
self.registry.append(subclass)
class RegistryABC(metaclass=RegistryABCMeta):
pass
You may now create categories just inheriting from RegistryABC
and using the register()
method:
class MyCategory(RegistryABC):
pass
MyCategory.register(tuple)
MyCategory.register(str)
print(MyCategory.registry)
and what you get here is [<class 'tuple'>, <class 'str'>]
.