Objects/typeobject.c (part 3)
Source:
cpython 3.14 @ ab2d84fe1023/Objects/typeobject.c
This annotation covers class creation and MRO computation. See objects_typeobject_detail for type_call, type_getattro, and slot inheritance, and objects_typeobject2_detail for type_repr, type_hash, and subtype checks.
Map
| Lines | Symbol | Role |
|---|---|---|
| 1-120 | type_new | Metaclass __call__: allocate, init, run __init_subclass__ |
| 121-320 | type_new_impl | Fill in tp_dict, slots, __doc__, __qualname__ |
| 321-480 | __init_subclass__ dispatch | Notify base classes that a subclass was created |
| 481-620 | __set_name__ dispatch | Notify descriptors of their name in the owning class |
| 621-900 | mro_implementation | C3 linearization for __mro__ |
| 901-1400 | mro_internal | Validate and install the MRO; detect inconsistencies |
Reading
type_new
// CPython: Objects/typeobject.c:3280 type_new
static PyObject *
type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
{
/* type(name, bases, namespace) -> new class */
PyObject *name, *bases, *namespace;
if (!PyArg_ParseTuple(args, "UO!O!", &name, &PyTuple_Type, &bases,
&PyDict_Type, &namespace)) return NULL;
return type_new_impl(metatype, name, bases, namespace, kwds);
}
type(name, bases, ns) with 3 args creates a new class. The 1-arg form type(obj) returns the type of obj.
type_new_impl
// CPython: Objects/typeobject.c:3340 type_new_impl
static PyObject *
type_new_impl(PyTypeObject *metatype, PyObject *name, PyObject *bases,
PyObject *namespace, PyObject *kwds)
{
/* 1. Determine the winner metatype (most-derived metaclass of all bases) */
PyTypeObject *winner = _PyType_CalculateMetaclass(metatype, bases);
/* 2. Allocate the type object */
PyTypeObject *type = (PyTypeObject *)metatype->tp_alloc(metatype, nslots);
/* 3. Copy the namespace dict into tp_dict */
type->tp_dict = Py_NewRef(namespace);
/* 4. Compute the MRO */
type->tp_mro = mro_internal(type, NULL);
/* 5. Inherit slots from bases */
type_ready(type);
/* 6. Run __set_name__ for all descriptors in namespace */
set_names(type);
/* 7. Call __init_subclass__ on each base */
init_subclass(type, kwds);
return (PyObject *)type;
}
_PyType_CalculateMetaclass selects the metaclass: if a base uses a custom metaclass, it must be a subtype of the metaclass of all other bases.
__init_subclass__
// CPython: Objects/typeobject.c:3520 init_subclass
static int
init_subclass(PyTypeObject *type, PyObject *kwds)
{
/* For each base in type.__mro__[1:], call
base.__init_subclass__(cls=type, **kwds). */
PyObject *super = PyObject_CallOneArg((PyObject *)&PySuper_Type,
(PyObject *)type);
PyObject *func = PyObject_GetAttr(super, &_Py_ID(__init_subclass__));
return PyObject_Call(func, PyTuple_New(0), kwds);
}
class Foo(Base, keyword=value): passes keyword=value to Base.__init_subclass__. Used by dataclasses, enum, and abc to hook into subclass creation.
__set_name__
// CPython: Objects/typeobject.c:3480 set_names
static int
set_names(PyTypeObject *type)
{
/* For each (key, value) in type.__dict__:
if hasattr(value, '__set_name__'):
value.__set_name__(type, key) */
PyObject *key, *value;
Py_ssize_t pos = 0;
while (PyDict_Next(type->tp_dict, &pos, &key, &value)) {
PyObject *set_name = PyObject_GetAttr(value, &_Py_ID(__set_name__));
if (set_name != NULL) {
PyObject_CallFunctionObjArgs(set_name, (PyObject *)type, key, NULL);
}
}
...
}
__set_name__ is called when a descriptor is added to a class body. Used by dataclasses.field(), SQLAlchemy column descriptors, etc.
C3 linearization (mro_implementation)
// CPython: Objects/typeobject.c:2180 mro_implementation
static PyObject *
mro_implementation(PyTypeObject *type)
{
/* Build the MRO using C3 linearization:
L[C] = C + merge(L[B1], L[B2], ..., [B1, B2, ...])
where B1, B2, ... are the direct bases of C. */
PyObject *bases = type->tp_bases;
Py_ssize_t n = PyTuple_GET_SIZE(bases);
/* Build lists: one per base + one for the bases themselves */
...
/* Iteratively extract good heads */
while (1) {
PyObject *candidate = find_good_head(lists, nlists);
if (candidate == NULL) {
/* Inconsistent MRO */
PyErr_SetString(PyExc_TypeError,
"Cannot create a consistent method resolution order (MRO) ...");
return NULL;
}
PyList_Append(mro, candidate);
remove_from_all(candidate, lists, nlists);
}
return PyList_AsTuple(mro);
}
C3 ensures that for any class C, its MRO respects the local precedence order of C's bases and the MROs of all bases. Diamond inheritance like class D(B, C) where B and C both inherit from A gives MRO [D, B, C, A, object].
gopy notes
type_new_impl is objects.TypeNew in objects/type.go. mro_implementation is objects.MROImplementation using C3 linearization. __init_subclass__ dispatch is objects.InitSubclass. __set_name__ dispatch is objects.SetNames.