By definition, we cannot instantiate an abstract class:
>>> import abc
>>> class A(abc.ABC):
... @abc.abstractmethod
... def f(self): raise NotImplementedError
...
>>> A()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class A with abstract method f
So isn’t it a contradiction that an instance of a concrete subclass is an instance of the abstract class?
>>> class B(A):
... def f(self): return 'foo'
...
>>> isinstance(B(), A)
True
There's a difference between an object being an instance of a class and the act of instantiating a class. Inheritance means that if B
is a subclass of A
, then isinstance(B(), A)
is true, even though B
, not A
, is the class being instantiated.
If you could never concretize an abstract class, there would be no point in defining the abstract class in the first place. The purpose of an abstract class is to provide an incomplete template for other classes; you can't simply use the abstract class as-is without doing making some additional definitions.
Put another way, given b = B()
, b
is an instance of A
and B
, but only B
is the type of b
. Is-type-of and is-instance-of are two different relations.