I remembered learning that type
is its own instance and that all new-style classes inherit from object
, so I started messing around:
issubclass(type, object) # True
issubclass(object, type) # False
# issubclass(cls, cls) is always True
type(type) is type # True
type(object) is type # True
To sum that up in words, type
like any new-style class is a subclass of object
, and like any class, both type
and object
are instances of type
. Here's a handy picture:
Big disclaimer here that I'm not a programmer and know little about the underlying mechanics of Python, so bear with me if I sound dumb. Normally a class has to be made before its instances. However, a superclass normally has to be made before its subclass. Applying those general Python rules to type
and object
makes an apparent chicken-egg paradox.
So which of these classes are made first? I also suspect that because these classes are implemented in a lower-level language, they don't have to follow Python rules.
From a language semantics perspective, both type
and object
exist, fully initialized, from the moment the program begins. Whatever the implementation does to get things into that state doesn't have to follow the rules of what the implementation allows you to do.
From a CPython implementation perspective, both type
and object
are statically allocated at C level, and neither is created first. You can see the variable definitions in Objects/typeobject.c
. Here's object
:
PyTypeObject PyBaseObject_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"object", /* tp_name */
sizeof(PyObject), /* tp_basicsize */
0, /* tp_itemsize */
object_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
object_repr, /* tp_repr */
...
};
and here's type
:
PyTypeObject PyType_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"type", /* tp_name */
sizeof(PyHeapTypeObject), /* tp_basicsize */
sizeof(PyMemberDef), /* tp_itemsize */
(destructor)type_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
(reprfunc)type_repr, /* tp_repr */
...
};
When interpreter initialization begins, both type
and object
are in a half-initialized state, and the PyType_Ready
function is responsible for finishing their initialization. The type pointers are set up in the variable definitions, but setting the superclass pointers is part of PyType_Ready
's job, and plenty of other initialization needs to be handled by PyType_Ready
- for example, the types don't have a __dict__
yet.
Incidentally, using some weird metaclasses and the fact that Python allows you to reassign __class__
on instances of user-defined classes, we can set up our own classes A
and B
where B
is a subclass of A
and both A
and B
are instances of B
, much like the situation with object
and type
. This is nothing like how object
and type
are actually created, though:
class DummyMeta(type):
pass
class A(type, metaclass=DummyMeta):
pass
class B(A):
pass
B.__class__ = B
A.__class__ = B
print(isinstance(A, B))
print(isinstance(B, B))
print(issubclass(B, A))
Output:
True
True
True