Search code examples
pythonooppython-internalsisinstance

How does isinstance work in python for subclasses?


Playing around with isinstance, I would like to know how it works for a subclass check.

class A:
    pass

class B(A):
    pass

class C(B):
    pass

print(isinstance(C(), C)) # True, I understand this fine
print(isinstance(C(), B)) # True, Ok, but how is this `True`?
print(isinstance(C(), A)) # True, this too?

What are the differences between type() and isinstance()? explains that isinstance does work for a subclass as seen in my second and third print statement. My question is not that, but, how does it work? When class A was defined it had no information but about the existence of class B or class C, likewise B had no information about C.

According to How does isinstance work for List? __instancecheck__ is called for isinstance, so if my class A was created before B and C how does this Python know that C() is indeed an instance of one of the subclass (B, subclass of A) or subclass of subclass of A (C, which is a subclass of B which itself is a subclass of A)?

Does something happen when I inherit a class in Python such that the base class knows that a child class exists? If that is the case can anyone let me know how this works?

PS : Do let me know if any clarification is needed. Many Thanks.


Solution

  • C() holds a reference to its type, C, and C has a tuple C.__mro__ of all its ancestors (including itself). The default type.__instancecheck__ looks through that tuple to see whether an object is an instance of a particular class.

    If you follow the function calls down from type.__instancecheck__, you eventually end up in this loop:

    for (i = 0; i < n; i++) {
        if (PyTuple_GET_ITEM(mro, i) == (PyObject *)b)
            return 1;
    

    Classes in Python do also have information about their subclasses - the C-level PyType_Ready function informs the base classes about a subclass while readying the subclass. However, searching the inheritance graph downward from the base class would produce pathologically slow behavior for cases where the second argument to isinstance has a lot of descendants.