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.
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.