Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How is __subclasses__ method implemented in CPython?

The docs say that:

Each class keeps a list of weak references to its immediate subclasses. This method returns a list of all those references still alive.

But how does each class obtain a list of weak references to its subclasses in the first place? In other words, when I create

class B(A):
  pass

how does A find out that B just subclassed it? And is this mechanism robust enough to survive edge cases (custom metaclasses, assignment to __bases__, etc.)?

like image 522
max Avatar asked Oct 20 '16 21:10

max


People also ask

What is __ subclasses __ in Python?

New-style classes i.e. subclassed from an object, which is the default in Python 3 have a __subclasses__ method. This method returns the subclasses of the class.

How do subclasses work in Python?

The process of creating a subclass of a class is called inheritance. All the attributes and methods of superclass are inherited by its subclass also. This means that an object of a subclass can access all the attributes and methods of the superclass.

How do you get a subclass in Python?

If you do have a string representing the name of a class and you want to find that class's subclasses, then there are two steps: find the class given its name, and then find the subclasses with __subclasses__ as above. However you find the class, cls. __subclasses__() would then return a list of its subclasses.

What does a subclass inherit from a superclass Python?

A subclass inherits everything from its superclass, which is referred to as inheritance in the object-orientation methodology and object-oriented programming. By inheritance, the superclass's attributes will not repeat in any of its subclasses.


1 Answers

As part of the initialization of a new class, a weak reference to that class is added to the tp_subclasses member of each of its base classes. You can see this in the Python source code in Objects/typeobject.c:

int
PyType_Ready(PyTypeObject *type)
{
    ...
    /* Link into each base class's list of subclasses */
    bases = type->tp_bases;
    n = PyTuple_GET_SIZE(bases);
    for (i = 0; i < n; i++) {
        PyObject *b = PyTuple_GET_ITEM(bases, i);
        if (PyType_Check(b) &&
            add_subclass((PyTypeObject *)b, type) < 0)
            goto error;
    }
    ...
}

static int
add_subclass(PyTypeObject *base, PyTypeObject *type)
{
    int result = -1;
    PyObject *dict, *key, *newobj;

    dict = base->tp_subclasses;
    if (dict == NULL) {
        base->tp_subclasses = dict = PyDict_New();
        if (dict == NULL)
            return -1;
    }
    assert(PyDict_CheckExact(dict));
    key = PyLong_FromVoidPtr((void *) type);
    if (key == NULL)
        return -1;
    newobj = PyWeakref_NewRef((PyObject *)type, NULL);
    if (newobj != NULL) {
        result = PyDict_SetItem(dict, key, newobj);
        Py_DECREF(newobj);
    }
    Py_DECREF(key);
    return result;
}

The setter for __bases__ also updates the subclass lists of each of the old and new bases:

static int
type_set_bases(PyTypeObject *type, PyObject *new_bases, void *context)
{
    ...
    if (type->tp_bases == new_bases) {
        /* any base that was in __bases__ but now isn't, we
           need to remove |type| from its tp_subclasses.
           conversely, any class now in __bases__ that wasn't
           needs to have |type| added to its subclasses. */

        /* for now, sod that: just remove from all old_bases,
           add to all new_bases */
        remove_all_subclasses(type, old_bases);
        res = add_all_subclasses(type, new_bases);
        update_all_slots(type);
    }
    ...
}

Note that if a metaclass does something to customize the meaning of the subclass relationship, __subclasses__ won't reflect that. For example, issubclass(list, collections.abc.Iterable) is True, but list won't show up in a search of the __subclasses__ tree starting from collections.abc.Iterable.

like image 105
user2357112 supports Monica Avatar answered Oct 10 '22 03:10

user2357112 supports Monica