Python course menu
Python Abstraction: Abstract Base Classes (ABC) and abstractmethod
Learn abstraction in Python: hide implementation details behind simple interfaces, define abstract base classes with the abc module and @abstractmethod, enforce required methods in subclasses, and combine abstraction with polymorphism.
On this page
Abstraction means exposing what an object does while hiding how it does it. You call payment.pay(499) without knowing whether the money moves by UPI, card or wallet. Python supports abstraction with abstract base classes (ABCs): classes that define a required interface but cannot be used on their own.
The four pillars of OOP#
Abstraction is the last of the four ideas that make up object-oriented programming:
An abstract base class#
The abc module provides ABC and the @abstractmethod decorator:
from abc import ABC, abstractmethod
class PaymentMethod(ABC):
@abstractmethod
def pay(self, amount):
...
def receipt(self, amount):
return f"Paid ₹{amount} via {self.pay(amount)}"
class UPI(PaymentMethod):
def __init__(self, upi_id):
self.upi_id = upi_id
def pay(self, amount):
return f"UPI ({self.upi_id})"
class Card(PaymentMethod):
def __init__(self, last4):
self.last4 = last4
def pay(self, amount):
return f"card ending {self.last4}"
for m in [UPI("asha@okbank"), Card("4321")]:
print(m.receipt(499))Paid ₹499 via UPI (asha@okbank)
Paid ₹499 via card ending 4321PaymentMethoddeclares that every payment method must havepay().- It can still provide shared, concrete methods like
receipt(). receipt()callsself.pay()without knowing which kind of payment it is: abstraction and polymorphism working together.
Abstract classes cannot be created#
An ABC is a template, not a finished class:
PaymentMethod()TypeError: Can't instantiate abstract class PaymentMethod without an implementation for abstract method 'pay'Subclasses must implement every abstract method#
If a subclass forgets one, Python refuses to create it, at creation time, not later when the missing method is finally called:
class Cash(PaymentMethod):
pass
Cash()TypeError: Can't instantiate abstract class Cash without an implementation for abstract method 'pay'This is the main advantage over a plain base class whose method just raises NotImplementedError: mistakes surface immediately.
Designing with interfaces#
Abstraction lets the rest of your program depend on an interface instead of a specific implementation. A storage example:
from abc import ABC, abstractmethod
class Storage(ABC):
@abstractmethod
def save(self, key, value): ...
@abstractmethod
def load(self, key): ...
class MemoryStorage(Storage):
def __init__(self):
self.data = {}
def save(self, key, value):
self.data[key] = value
def load(self, key):
return self.data.get(key)
store = MemoryStorage()
store.save("theme", "dark")
print(store.load("theme"), store.load("font"))dark NoneLater you could add FileStorage or DatabaseStorage. Any code written against Storage keeps working, and tests can use the fast MemoryStorage.
When to use an ABC#
- Several classes must provide the same set of methods (payment methods, storage back-ends, file exporters).
- You want missing methods caught as early as possible.
- You are writing a framework or library that others will extend.
For one or two small classes, plain inheritance or duck typing is usually enough.
Common mistakes#
Practice#
Practice exercise
Create an abstract Shape class with abstract methods area() and perimeter(), and a concrete describe() method returning Circle: area=12.57, perimeter=12.57 style text (2 decimal places). Implement Circle(r) and Rect(w, h) and describe a circle of radius 2 and a 3 × 4 rectangle.
Show hint
describe can use type(self).__name__ and call self.area() and self.perimeter().
Show solution
from abc import ABC, abstractmethod
import math
class Shape(ABC):
@abstractmethod
def area(self): ...
@abstractmethod
def perimeter(self): ...
def describe(self):
return (f"{type(self).__name__}: area={self.area():.2f}, "
f"perimeter={self.perimeter():.2f}")
class Circle(Shape):
def __init__(self, r):
self.r = r
def area(self):
return math.pi * self.r ** 2
def perimeter(self):
return 2 * math.pi * self.r
class Rect(Shape):
def __init__(self, w, h):
self.w, self.h = w, h
def area(self):
return self.w * self.h
def perimeter(self):
return 2 * (self.w + self.h)
for s in [Circle(2), Rect(3, 4)]:
print(s.describe())Circle: area=12.57, perimeter=12.57
Rect: area=12.00, perimeter=14.00For a circle of radius 2, the area (4π) and the circumference (4π) happen to be equal.
Key takeaway#
Finished this lesson?