22

I'm trying to understand the benefits of using abstract base classes. Consider these two pieces of code:

Abstract base class:

from abc import ABCMeta, abstractmethod, abstractproperty class CanFly: __metaclass__ = ABCMeta @abstractmethod def fly(self): pass @abstractproperty def speed(self): pass class Bird(CanFly): def __init__(self): self.name = 'flappy' @property def speed(self): return 1 def fly(self): print('fly') b = Bird() print(isinstance(b, CanFly)) # True print(issubclass(Bird, CanFly)) # True 

Plain inheritance:

class CanFly(object): def fly(self): raise NotImplementedError @property def speed(self): raise NotImplementedError() class Bird(CanFly): @property def speed(self): return 1 def fly(self): print('fly') b = Bird() print(isinstance(b, CanFly)) # True print(issubclass(Bird, CanFly)) # True 

As you see, both methods support inflection using isinstance and issubclass.

Now, one difference I know is that, if you try to instantiate a subclass of an abstract base class without overriding all abstract methods/properties, your program will fail loudly. However, if you use plain inheritance with NotImplementedError, your code won't fail until you actually invoke the method/property in question.

Other than that, what makes using abstract base class different?

2
  • possible duplicate of Why use Abstract Base Classes in Python? Commented Mar 17, 2014 at 8:01
  • 2
    @JayanthKoushik I don't think this question is a duplicate of that question, because this is asking about abstract base classes vs. plain inheritance, while the other question is asking about abstract base classes vs. duck typing. Commented Mar 15, 2018 at 13:19

1 Answer 1

13

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 
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.