The most notable answer in terms of concrete specifics, besides what you mentioned in your question, is that the presence of the @abstractmethod or @abstractproperty1 decorators, along with inheriting from ABC (or having the ABCMeta metaclass) prevents you from instantiating the object at all.
from abc import ABC, abstractmethod class AbsParent(ABC): @abstractmethod def foo(self): pass AbsParent() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: Can't instantiate abstract class AbsParent with abstract methods foo
However, there's more at play here. Abstract Base Classes were introduced to Python in PEP 3119. I'd recommend reading through the "Rationale" section for Guido's take on why they were introduced in the first place. My sophomoric summary would be that they're less about their concrete features and more about their philosophy. Their purpose is to signal to external inspectors that the object is inheriting from the ABC, and because it's inheriting from an ABC it will follow a good-faith agreement. This "good-faith agreement" is that the child object will follow the intention of the parent. The actual implementation of this agreement is left up to you, which is why it's a good-faith agreement, and not an explicit contract.
This primarily shows up through the lens of the register() method. Any class that has ABCMeta as its metaclass (or simply inherits from ABC) will have a register() method on it. By registering a class with an ABC you are signaling that it inherits from the ABC, even though it technically doesn't. This is where the good-faith agreement comes in.
from abc import ABC, abstractmethod class MyABC(ABC): @abstractmethod def foo(self): """should return string 'foo'""" pass class MyConcreteClass(object): def foo(self): return 'foo' assert not isinstance(MyConcreteClass(), MyABC) assert not issubclass(MyConcreteClass, MyABC)
While MyConcreteClass, at this point is unrelated to MyABC, it does implement the API of MyABC according to the requirements laid out in the comments. Now, if we register MyConcreteClass with MyABC, it will pass isinstance and issubclass checks.
MyABC.register(MyConcreteClass) assert isinstance(MyConcreteClass(), MyABC) assert issubclass(MyConcreteClass, MyABC)
Again, this is where the "good-faith agreement" comes into play. You do not have to follow the API laid out in MyABC. By registering the concrete class with the ABC we are telling any external inspectors that we, the programmers, are adhering to the API we're supposed to.
1 note that @abstractproperty is no longer preferred. Instead you should use:
@property @abstractmethod def foo(self): pass