Abstract Classes & Interfaces
Lesson 3 of 5
3Abstract Classes & Interfaces
The big pictureEnforcing a contract on everyone who inherits from you, how an abstract class differs from an interface, and why Python refuses to instantiate an abstract one.
What the picture shows: Cash writes pay and gets created, Broken forgets it and Python refuses it with TypeError before it is ever used. Wallet does not inherit at all but owns pay, so it works wherever pay is asked for.
Map of this part
The concepts in order, click any one to jump to it:
Two ways into the same idea
An abstract class is like an employment contract: it says "every employee must hand in a weekly report" without writing the report. You cannot hire "the contract", only a real employee who signed it and does what it says. That way everyone who comes later has what you need.
ABC and @abstractmethod from the abc module make a method mandatory, and Python refuses to create an object of a class that has not implemented every abstract method. An interface is a contract with no implementation; in Python it can also be a typing.Protocol, based on shape (duck typing) rather than inheritance.
Before You Start
🧬The inheritance lesson
You know how a child redefines its parent's method.
📝The idea of a contract
A written agreement: whoever signs is bound by it.
The Core Idea
Sometimes you want to say: every shape must know its area, but I do not know how each shape computes it. This part is about writing the contract before the implementation, in three examples.
1A parent that does not know the answer
The Shape class declares area but raises an error if it is called directly. Children such as Square write the real computation.
class Shape:
def area(self):
raise NotImplementedError("every shape computes its own area")
class Square(Shape):
def __init__(self, side):
self.side = side
def area(self):
return self.side * self.side
print(Square(3).area())
try:
print(Shape().area())
except NotImplementedError as error:
print("error:", error)
This is the simplest form of a contract: the parent says "you must write this" and the child writes it. But the error shows up late, at the call, not at creation.
2Make the contract mandatory
ABC and abstractmethod from the abc module make Python refuse to create a Shape at all, and refuse any child that did not write area.
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
class Circle(Shape):
def __init__(self, r):
self.r = r
def area(self):
return round(3.14159 * self.r * self.r, 1)
print(Circle(2).area())
try:
Shape()
except TypeError:
print("an abstract shape cannot be created")
The difference from the previous step: the error appears early, at the moment of creation, which is better because you find it before a user does.
3A contract without inheritance
Duck and Robot inherit from neither each other nor a shared parent, yet both have sound, so play works with both.
class Duck:
def sound(self):
return "quack"
class Robot:
def sound(self):
return "beep"
def play(thing):
print(thing.sound())
play(Duck())
play(Robot())
Python cares about what an object can do, not what type it is. This is called duck typing, and it is why interfaces in Python are lighter than in other languages.
Child(), and the message names the missing method. Read it; it tells you exactly what to write.How It Works, Step by Step: When Python stops you making an object
The abstract class declares methods with @abstractmethod and no real body.
A class inherits from it and writes some or all of them.
At Cash() Python checks: is any abstract method still unwritten?
If one is left it refuses with TypeError; otherwise it makes the object.
Going deeper
Sometimes you want to force every child class to implement a specific method, without writing its implementation yourself in the parent class. This is where abstract classes come in.
Abstract Base Class (ABC)
A class that can't have an object created from it directly; defined by inheriting ABC from the abc module.
@abstractmethod
A method with no implementation in the parent class, any child class that doesn't implement it remains abstract too, and can't have an object created from it.
Duck Typing
Python doesn't always enforce explicit inheritance: "if the object has the required method, it's usable", regardless of its origin.
Mixin
A small class that adds one specific behavior (like Logging) through multiple inheritance, without representing a complete concept on its own.
Diagram 7, PaymentMethod is abstract and forces pay() to exist. LoggedCreditCardPayment inherits from two classes together: the core behavior plus extra Mixin behavior.
from abc import ABC, abstractmethod
class PaymentMethod(ABC):
@abstractmethod
def pay(self, amount):
"""Every child class must implement this method its own way."""
class CreditCardPayment(PaymentMethod):
def pay(self, amount):
return f"Paid {amount} via credit card"
class CashPayment(PaymentMethod):
def pay(self, amount):
return f"Paid {amount} in cash"
# PaymentMethod() -> TypeError: can't instantiate an abstract class
## Duck Typing: any object with pay() is usable, no need to inherit PaymentMethod
class GiftCard:
def pay(self, amount):
return f"Deducted {amount} from the gift card"
def checkout(payment_method, amount):
print(payment_method.pay(amount)) # doesn't care about the object's type, just that it has pay()
checkout(CreditCardPayment(), 200)
checkout(GiftCard(), 50) # works perfectly even though it doesn't inherit from PaymentMethod
## Mixin: adding logging behavior via multiple inheritance
class LoggableMixin:
def log_action(self, msg):
print(f"[LOG] {msg}")
class LoggedCreditCardPayment(CreditCardPayment, LoggableMixin):
def pay(self, amount):
self.log_action(f"Attempting payment of {amount}")
return super().pay(amount)
| Tool | When to use it |
|---|---|
Abstract Class + @abstractmethod | You want to enforce a strict contract: "anyone inheriting this must implement these methods" |
| Duck Typing | You want maximum flexibility without tying the code to a specific inheritance chain; "it just needs to work the same way" |
| Mixin | You want to add one reusable behavior across unrelated classes (logging, comparison, JSON conversion...) |
@abstractmethod above a method in a class that inherits ABC, without it, Python treats the class as ordinary and it can be instantiated directly, silently defeating the goal of "enforcing the contract".
Check your understanding: why does the checkout() function work with GiftCard even though it doesn't inherit from PaymentMethod at all?
Because of Duck Typing: Python doesn't check the object's type or inheritance tree when calling payment_method.pay(amount), it only checks that the object has a method named pay that accepts this number of arguments, and that's enough for the operation to succeed.
Worked example
class Payment(ABC) with @abstractmethod def pay(self, amount), and each method of payment a class that inherits it. The checkout code only deals with Payment, and a new method does not change it.Myth vs Reality
| Myth | Reality |
|---|---|
| An abstract class is useless because you cannot make an object from it. | Its use is forcing every child to write the required methods, so Python finds the gap at creation time, not halfway through a run. |
| Python needs inheritance to accept an object somewhere. | It is enough for the object to have the needed method; that is duck typing: if it walks like a duck and quacks like a duck, it is a duck. |
| A mixin is just an ordinary class. | A mixin is a small class that adds one ability, like printing as JSON, and is never used alone, only mixed into other classes. |
Try it yourself
Base(ABC) has two abstract methods. Child(Base) wrote only one. What happens on Child()?
A class stays abstract as long as one abstract method is unwritten, and Python refuses to create an object from it with a message naming the missing method. The definition itself passes without error; the refusal happens at creation.
Quick Review Flashcards
Check Your Understanding
When does Python raise TypeError with an abstract class?
When you try to make an object of a class that still has an unwritten abstract method.
What is the difference between ABC and duck typing?
ABC is an explicit contract checked at creation; duck typing is an implicit agreement: the method just has to exist at call time.
Why is a mixin never used alone?
Because it adds one ability and relies on data supplied by the class it is mixed into.
Resources for this lesson
Grouped by type. Start with the official docs for precision, or an article or video for the explanation.
Official docs3
Done with this lesson?
Sign in to save your progress.