Python base class can be object without __mro_entries__

Viewed 204

Today I have discovered that python object without __mro_entries__ can be used as a base class.

Example:

class Base:
    def __init__(self, *args):
        self.args = args

    def __repr__(self):
        return f'{type(self).__name__}(*{self.args!r})'


class Delivered(Base):
    pass


b = Base()
d = Delivered()


class Foo(b, d):
    pass


print(type(Foo) is Delivered)
print(Foo)
True
Delivered(*('Foo', (Base(*()), Delivered(*())), {'__module__': '__main__', '__qualname__': 'Foo'}))

As a result Foo will be instance of a Delivered class and it's not a valid type.

I do understand use case of __mro_entries__ but what use case of using object without __mro_entries__ as a base class. Is it a bug at python?

1 Answers

TL;DR Not a bug, but an extreme abuse of the class statement.


A class statement is equivalent to a call to a metaclass. Lacking an explicit metaclass keyword argument, the metaclass has to be inferred from the base class(es). Here, the "metaclass" of the "class" b is Base, while the metaclass of d is Delivered. Since each is a non-strict subclass of a common metaclass (Base), Delivered is chosen as the more specific metaclass.

>>> Delivered('Foo', (b, d), {})
Delivered(*('Foo', (Base(*()), Delivered(*())), {}))

Delivered can be used as a metaclass because it accepts the same arguments that the class statement expects a metaclass to accept: a string for the name of the type, a sequence of parent classes, and a mapping to use as the namespace. In this case, Delivered doesn't use them to create a type; it simply prints the arguments.

As a result, Foo is bound to an instance of Delivered, not a type. So Foo is a class only in the sense that it was produced by a class statement: it is decidedly not a type.

>>> issubclass(Foo, Delivered)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: issubclass() arg 1 must be a class
>>> Foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'Delivered' object is not callable
Related