pyguides

abc module

The abc module provides the building blocks for abstract base classes (ABCs) in Python. ABCs let you define a class that can’t be instantiated on its own — subclasses must provide implementations for any methods marked as abstract. It’s the standard way to enforce interface contracts in Python.

Defining an ABC

The simplest approach is to inherit from ABC:

from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self) -> float:
        """Calculate and return the shape's area."""
        pass

# TypeError: Can't instantiate abstract class Shape with abstract method area
# s = Shape()

A class with any abstract methods cannot be instantiated. Subclasses must implement all abstract methods before they can be instantiated.

The Two Ways to Create an ABC

Option 1 — inherit from ABC:

from abc import ABC, abstractmethod

class MyABC(ABC):
    pass

Option 2 — use ABCMeta as metaclass:

from abc import ABCMeta, abstractmethod

class MyABC(metaclass=ABCMeta):
    pass

Both produce the same result. Inheriting from ABC is generally cleaner.

Abstract Methods with Implementation

Unlike Java, Python’s abstract methods can have a body. When a subclass calls super(), that implementation runs — useful as an endpoint in cooperative multiple inheritance chains:

class Animal(ABC):
    @abstractmethod
    def speak(self) -> str:
        # Abstract but has a default: subclasses must override but can call super()
        return "some sound"

class Dog(Animal):
    def speak(self) -> str:
        return super().speak()  # returns "some sound" if Dog didn't override

Abstract Properties

Stack @property with @abstractmethod for abstract read-only properties:

from abc import ABC, abstractmethod

class Container(ABC):
    @property
    @abstractmethod
    def size(self) -> int:
        """Return the number of items in the container."""
        pass

For read-write properties, mark the setter as abstract too:

class Container(ABC):
    @property
    def size(self) -> int:
        return self._size

    @size.setter
    @abstractmethod
    def size(self, value: int) -> None:
        pass

class ListContainer(Container):
    def __init__(self, items):
        self._items = items

    @property
    def size(self) -> int:
        return len(self._items)

    @size.setter
    def size(self, value: int) -> None:
        self._items = [None] * value

Abstract classmethod and staticmethod

The decorators stack with @abstractmethod. Put @abstractmethod innermost:

class Config(ABC):
    @classmethod
    @abstractmethod
    def from_env(cls) -> "Config":
        """Load configuration from environment variables."""
        pass

    @staticmethod
    @abstractmethod
    def validate(data: dict) -> bool:
        """Validate configuration data."""
        pass

Note: abstractclassmethod and abstractstaticmethod exist but are deprecated since Python 3.3. Use the stacking approach above instead.

Virtual Subclasses with register()

You can register a class as a “virtual subclass” of an ABC without using inheritance. The issubclass() and isinstance() checks will recognize it:

from abc import ABC

class MySequence(ABC):
    pass

# Register built-in tuple as a virtual subclass
MySequence.register(tuple)

issubclass(tuple, MySequence)  # True
isinstance((), MySequence)     # True

Virtual subclasses don’t appear in the MRO, and the ABC’s methods aren’t callable via super() on them. Only the issubclass()/isinstance() relationship is established.

Customizing issubclass with subclasshook

Override __subclasshook__ to implement custom subclass detection logic:

from abc import ABC, abstractmethod

class SupportsIter(ABC):
    @abstractmethod
    def __iter__(self):
        pass

    @classmethod
    def __subclasshook__(cls, C):
        if cls is SupportsIter:
            # Any class with __iter__ in its MRO qualifies
            if any("__iter__" in B.__dict__ for B in C.__mro__):
                return True
        return NotImplemented

__subclasshook__ must return True, False, or NotImplemented (which falls back to normal inheritance checks).

Dynamic Abstract Method Updates

If you modify abstract methods on a class after definition, call update_abstractmethods() to recalculate its abstraction status:

from abc import ABC, abstractmethod, update_abstractmethods

class Foo(ABC):
    pass

# Dynamically add an abstract method
Fooabstractmethod = abstractmethod(lambda self: None)
Foo.__abstractmethods__ = {"my_method"}
update_abstractmethods(Foo)  # Forces Python to recheck abstraction

get_cache_token

Returns a token identifying the current state of the ABC cache. The token changes whenever any ABC calls register():

from abc import get_cache_token

token = get_cache_token()  # opaque object, supports equality testing

Used mainly by framework authors to detect when ABC registrations have changed.

When ABCs Beat Protocols

Use ABCs when you need:

  • Explicit enforcement (can’t instantiate without implementing abstract methods)
  • Virtual subclass registration (register()) for existing classes you don’t control
  • Cross-module interface contracts

For general duck typing and structural subtyping (does it have __len__?), Protocol from typing is often simpler.

See Also

  • isinstance() — check object types and ABC membership
  • iter() — works with any ABC-derived iterable
  • Protocol classes — Python’s structural subtyping via typing.Protocol