From the docs:
The appropriate metaclass for a class definition is determined as
follows:
if no bases and no explicit metaclass are given, then type() is used;
if an explicit metaclass is given and it is not an instance of type(), then it is used directly as the metaclass;
if an instance of type() is given as the explicit metaclass, or bases are defined, then the most derived metaclass is used.
The most derived metaclass is selected from the explicitly specified
metaclass (if any) and the metaclasses (i.e. type(cls)) of all
specified base classes. The most derived metaclass is one which is a
subtype of all of these candidate metaclasses.
In your case above, the third bullet holds, as type is given as the explicit metaclass and there are base-classes.
According to the definition of the most derived metaclass, the candidates are: TestMeta and type. Since TestMeta inherits type, it is the most derived metaclass and is indeed the metaclass of Child.
To demonstrate the difference, the following code shows how we can "force" Child to get the specified metaclass instead of the inherited one:
class TestMeta(type):
def __init__(cls, classname, bases, dict_):
print(f'In TestMeta, class {cls}')
type.__init__(cls, classname, bases, dict_)
class OtherMeta(TestMeta):
def __init__(cls, classname, bases, dict_):
print(f'In OtherMeta, class {cls}')
type.__init__(cls, classname, bases, dict_)
class Parent(metaclass=TestMeta):
pass
class Child(Parent, metaclass=OtherMeta):
pass
The output would be:
In TestMeta, class <class '__main__.Parent'>
In OtherMeta, class <class '__main__.Child'>
Notice that again, according to the definition of the most derived metaclass, this only works if OtherMeta itself is a subclass of TestMeta. Removing this inheritance relation will give the error:
TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases