Python's duck typing philosophy means you can call any method on any object and find out at runtime whether it works. That flexibility is powerful, but in larger codebases it creates a problem: nothing stops a developer from subclassing a base class and forgetting to implement a critical method. The error only surfaces when the missing method is called, possibly deep inside production code. The @abstractmethod decorator from the abc module solves this by moving the check to instantiation time. If a subclass has not overridden every abstract method, Python refuses to create an instance and raises a TypeError immediately.
Setting Up an Abstract Base Class
An abstract base class (ABC) is a class that cannot be instantiated directly. You create one by inheriting from abc.ABC and decorating one or more methods with @abstractmethod:
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
"""Calculate and return the area of the shape."""
pass
@abstractmethod
def perimeter(self):
"""Calculate and return the perimeter of the shape."""
pass
def describe(self):
return f"{self.__class__.__name__}: area={self.area()}, perimeter={self.perimeter()}"
The Shape class defines two abstract methods (area and perimeter) and one concrete method (describe). The concrete method can call the abstract methods because by the time describe runs, the object is guaranteed to be an instance of a subclass that has implemented both.
What Happens When a Subclass Forgets
If a subclass does not override every abstract method, Python blocks instantiation:
class IncompleteCircle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14159 * self.radius ** 2
# perimeter is NOT implemented
try:
c = IncompleteCircle(5)
except TypeError as e:
print(f"Error: {e}")
The error names the missing method. This makes debugging straightforward. The class definition itself succeeds — the TypeError is raised only when you try to create an instance. This means you can define intermediate abstract classes that inherit from other abstract classes without implementing all the methods.
Implementing the Abstract Method
A complete subclass provides concrete implementations for every abstract method:
import math
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return math.pi * self.radius ** 2
def perimeter(self):
return 2 * math.pi * self.radius
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
def perimeter(self):
return 2 * (self.width + self.height)
shapes = [Circle(5), Rectangle(4, 7)]
for s in shapes:
print(s.describe())
Both subclasses implement area and perimeter, so they can be instantiated. The inherited describe method works on both because it calls the abstract methods polymorphically.
Abstract Methods Can Have a Body
Unlike abstract methods in Java or pure virtual functions in C++, Python's abstract methods can contain a default implementation. Subclasses must still override them, but they can call the base implementation through super():
from abc import ABC, abstractmethod
class Serializer(ABC):
@abstractmethod
def serialize(self, data):
"""Base validation shared by all serializers."""
if not isinstance(data, dict):
raise TypeError("Data must be a dictionary")
class JsonSerializer(Serializer):
def serialize(self, data):
super().serialize(data) # run the base validation
import json
return json.dumps(data, indent=2)
class CsvSerializer(Serializer):
def serialize(self, data):
super().serialize(data)
headers = ",".join(data.keys())
values = ",".join(str(v) for v in data.values())
return f"{headers}\n{values}"
js = JsonSerializer()
print(js.serialize({"name": "Kandi", "role": "engineer"}))
cs = CsvSerializer()
print(cs.serialize({"name": "Kandi", "role": "engineer"}))
Putting shared validation or setup logic in the abstract method's body is a useful pattern. Subclasses call super().method() to get the shared behavior and then add their own specific logic. This avoids duplicating the validation in every subclass.
Abstract Properties
You can combine @property with @abstractmethod to require subclasses to provide a specific attribute through the property interface. The @abstractmethod decorator must be the innermost (closest to def):
from abc import ABC, abstractmethod
class Vehicle(ABC):
@property
@abstractmethod
def fuel_type(self):
"""Return the type of fuel this vehicle uses."""
pass
@property
@abstractmethod
def max_speed_kmh(self):
"""Return the maximum speed in km/h."""
pass
def summary(self):
return f"{self.__class__.__name__}: {self.fuel_type}, max {self.max_speed_kmh} km/h"
class ElectricCar(Vehicle):
@property
def fuel_type(self):
return "electric"
@property
def max_speed_kmh(self):
return 200
class DieselTruck(Vehicle):
@property
def fuel_type(self):
return "diesel"
@property
def max_speed_kmh(self):
return 120
print(ElectricCar().summary())
print(DieselTruck().summary())
Combining with @classmethod and @staticmethod
You can also require subclasses to implement class methods or static methods. The stacking order matters: @abstractmethod must be the innermost decorator:
from abc import ABC, abstractmethod
class Parser(ABC):
@classmethod
@abstractmethod
def from_string(cls, raw):
"""Parse a raw string and return an instance."""
pass
@staticmethod
@abstractmethod
def file_extension():
"""Return the file extension this parser handles."""
pass
class JsonParser(Parser):
def __init__(self, data):
self.data = data
@classmethod
def from_string(cls, raw):
import json
return cls(json.loads(raw))
@staticmethod
def file_extension():
return ".json"
p = JsonParser.from_string('{"key": "value"}')
print(p.data)
print(JsonParser.file_extension())
When combining @abstractmethod with @classmethod, @staticmethod, or @property, always place @abstractmethod as the innermost decorator (directly above def). Reversing the order can cause the method to not be recognized as abstract.
Multiple Abstract Methods
A class can have as many abstract methods as needed. A subclass must implement all of them, or it remains abstract itself. This lets you build layered hierarchies where intermediate classes implement some methods and leave others for further subclasses:
from abc import ABC, abstractmethod
class DataSource(ABC):
@abstractmethod
def connect(self):
pass
@abstractmethod
def fetch(self, query):
pass
@abstractmethod
def close(self):
pass
class SqlDataSource(DataSource):
"""Implements connect and close, leaves fetch abstract."""
def connect(self):
print("SQL connection established")
def close(self):
print("SQL connection closed")
# SqlDataSource is still abstract because fetch is not implemented
try:
SqlDataSource()
except TypeError as e:
print(f"Still abstract: {e}")
class PostgresSource(SqlDataSource):
def fetch(self, query):
return f"Postgres result for: {query}"
pg = PostgresSource()
pg.connect()
print(pg.fetch("SELECT * FROM users"))
pg.close()
Inspecting __abstractmethods__
Every class that inherits from an ABC has a __abstractmethods__ attribute — a frozen set of the method names that have not yet been implemented. You can inspect it to see what a subclass still needs to provide:
print(f"Shape requires: {Shape.__abstractmethods__}")
print(f"SqlDataSource requires: {SqlDataSource.__abstractmethods__}")
print(f"PostgresSource requires: {PostgresSource.__abstractmethods__}")
An empty frozenset means all abstract methods have been implemented and the class can be instantiated.
Abstract Classes vs Duck Typing
Python developers often debate whether to enforce interfaces with ABCs or rely on duck typing. Both approaches are valid, and the choice depends on the situation:
| Consideration | @abstractmethod / ABC | Duck Typing |
|---|---|---|
| Error timing | At instantiation | At method call |
| Enforcement | Compile-time-like guarantee | No enforcement |
| Flexibility | Requires inheritance hierarchy | Works with any object that has the method |
| Team scale | Useful for large teams and public APIs | Sufficient for small teams and internal code |
| Discovery | __abstractmethods__ shows what is required | Requires reading docs or source code |
| Runtime overhead | Minimal (checked once at instantiation) | None |
ABCs and duck typing are not mutually exclusive. Python's built-in ABCs like collections.abc.Iterable use a mechanism called virtual subclassing. You can register a class as a "virtual subclass" of an ABC without inheriting from it, using ABC.register(). This combines the interface enforcement of ABCs with the flexibility of duck typing.
Key Takeaways
- @abstractmethod prevents incomplete subclasses from being instantiated: Any class that inherits from an ABC must override every method marked with
@abstractmethod. Attempting to create an instance without doing so raisesTypeErrornaming the missing methods. - Inherit from abc.ABC: The simplest way to create an abstract class is to inherit from
ABC. This is equivalent to usingmetaclass=ABCMetabut cleaner. - Abstract methods can have bodies: The base class can provide default logic. Subclasses are still required to override the method, but they can call
super()to reuse the default. - Stack decorators with @abstractmethod innermost: When combining with
@property,@classmethod, or@staticmethod, place@abstractmethoddirectly abovedef. - Intermediate abstract classes are valid: A subclass that implements only some abstract methods remains abstract itself and cannot be instantiated. Further subclasses must complete the remaining methods.
- Inspect __abstractmethods__ for debugging: This frozenset on any ABC-derived class shows which methods still need to be implemented.
- Choose ABCs for public APIs and large teams: Use
@abstractmethodwhen you want the enforcement and discoverability of a defined interface. Use duck typing when flexibility and simplicity matter more.
The @abstractmethod decorator is Python's mechanism for enforcing interface contracts at the earliest possible moment. It bridges the gap between Python's dynamic nature and the structural guarantees that larger projects need.
Frequently Asked Questions
What does @abstractmethod do in Python?
The @abstractmethod decorator marks a method in an abstract base class (ABC) as required. Any subclass that inherits from the ABC must provide its own implementation of that method before it can be instantiated. Attempting to create an instance of a class that has not overridden all abstract methods raises a TypeError.
Can an abstract method have an implementation in the base class?
Yes. Unlike abstract methods in Java or C++, Python's @abstractmethod allows the base class method to contain a body with code. Subclasses are still required to override the method, but they can call the base class implementation through super() to extend rather than replace the behavior.
How do I combine @abstractmethod with @property?
Stack @property above @abstractmethod on the same method. The @abstractmethod decorator must be the innermost (closest to the def). Subclasses must then define a concrete @property with the same name. This pattern enforces that all subclasses provide a specific attribute through the property interface.
What is the difference between ABC and ABCMeta?
ABC is a helper class that uses ABCMeta as its metaclass. Inheriting from ABC is the modern, simplified way to create an abstract class. Using ABCMeta directly as a metaclass achieves the same result but requires the metaclass=ABCMeta syntax. Both approaches are functionally equivalent.