Abstract Base Classes with abc

· 3 min read · Updated March 13, 2026 · intermediate
python oop abc classes inheritance

Abstract base classes (ABCs) provide a way to define interfaces in Python. They let you specify which methods a subclass must implement without providing a full implementation yourself. The abc module, part of Python standard library, enables this pattern.

Why Use Abstract Base Classes?

When you create a class hierarchy, you sometimes want to define a base class that establishes a contract: any subclass must provide certain methods. Regular Python classes do not enforce this. Subclasses can omit methods, and your code will only fail at runtime when those methods are called.

Abstract base classes solve this problem by preventing instantiation of any subclass that has not implemented all required methods. The check happens at subclass creation time, not when you try to use the class.

Defining an Abstract Base Class

Use the ABC class and @abstractmethod decorator from the abc module:

from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self) -> float:
        pass
    
    @abstractmethod
    def perimeter(self) -> float:
        pass

The Shape class cannot be instantiated directly:

try:
    s = Shape()
except TypeError as e:
    print(e)
# Can't instantiate abstract class Shape with abstract method area

Subclasses must implement both methods or they also become abstract and cannot be instantiated:

class Circle(Shape):
    def __init__(self, radius: float):
        self.radius = radius
    
    def area(self) -> float:
        return 3.14159 * self.radius ** 2
    
    def perimeter(self) -> float:
        return 2 * 3.14159 * self.radius

# This works
circle = Circle(5)
print(circle.area())
# 78.53975

Combining Abstract and Concrete Methods

Your abstract class can include both abstract and concrete methods. Concrete methods can use self and call abstract methods, knowing they will be provided by subclasses:

class Animal(ABC):
    @abstractmethod
    def speak(self) -> str:
        pass
    
    def announce(self) -> str:
        return f"The animal says: {self.speak()}"

class Dog(Animal):
    def speak(self) -> str:
        return "woof"

class Cat(Animal):
    def speak(self) -> str:
        return "meow"

dog = Dog()
print(dog.announce())
# The animal says: woof

Abstract Properties

You can also define abstract properties using the @abstractproperty decorator or by combining @property with @abstractmethod:

from abc import ABC, abstractmethod

class Container(ABC):
    @property
    @abstractmethod
    def size(self) -> int:
        pass
    
    @abstractmethod
    def is_empty(self) -> bool:
        pass

class Box(Container):
    def __init__(self):
        self._items = []
    
    @property
    def size(self) -> int:
        return len(self._items)
    
    def is_empty(self) -> bool:
        return len(self._items) == 0
    
    def add(self, item):
        self._items.append(item)

box = Box()
print(box.is_empty())
# True
box.add("something")
print(box.size)
# 1

Abstract Class Methods and Static Methods

Abstract classes can include class methods and static methods. The abstract method can be of any type:

from abc import ABC, abstractmethod

class Parser(ABC):
    @abstractmethod
    @classmethod
    def from_string(cls, s: str) -> "Parser":
        pass

class JSONParser(Parser):
    @classmethod
    def from_string(cls, s: str) -> "JSONParser":
        return cls()

json_parser = JSONParser.from_string('{"key": "value"}')
print(type(json_parser).__name__)
# JSONParser

Registering Virtual Subclasses

You can register classes as virtual subclasses of an ABC without using inheritance. This is useful for adapting existing classes to your interface:

class Format(ABC):
    @abstractmethod
    def convert(self, data: str) -> str:
        pass

# Register a class as a subclass
@Format.register
class UppercaseFormat:
    def convert(self, data: str) -> str:
        return data.upper()

# It now passes isinstance checks
upper = UppercaseFormat()
print(isinstance(upper, Format))
# True
print(upper.convert("hello"))
# HELLO

When to Use Abstract Base Classes

Abstract base classes work well when you need to:

  1. Define a common interface for related classes
  2. Enforce method implementation in subclasses
  3. Create polymorphic code that works with any subclass
  4. Document expected method signatures

For simple cases where you only need to check if an object has certain methods, consider duck typing with hasattr or the Protocol class from typing instead. Protocols provide structural subtyping without explicit inheritance.

See Also