@abstractmethod: Requiring Subclasses to Implement a Method Before They Can Be Instantiated

A scenario you may recognize

A team ships a plugin system. The base class is well-documented: subclasses should implement process(). Two months later, a new developer adds a plugin, misses the docstring, and ships without process(). The bug reaches production — not at startup, but at 2 AM when a specific job runs and hits the unimplemented method.

This is the exact problem @abstractmethod solves. The check moves from "when the missing method is called" to "when the object is created" — making the mistake impossible to ship.

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. The mechanism was introduced in PEP 3119, authored by Guido van Rossum and Talin in 2007, and has been available since Python 2.6 and 3.0.

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) -> float:
        """Calculate and return the area of the shape."""
        ...

    @abstractmethod
    def perimeter(self) -> float:
        """Calculate and return the perimeter of the shape."""
        ...

    def describe(self) -> str:
        return f"{self.__class__.__name__}: area={self.area()}, perimeter={self.perimeter()}"
Mental Model: The Enforcement Chain

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}")
Output Error: Can't instantiate abstract class IncompleteCircle without an implementation for abstract method 'perimeter'

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: float) -> None:
        self.radius = radius

    def area(self) -> float:
        return math.pi * self.radius ** 2

    def perimeter(self) -> float:
        return 2 * math.pi * self.radius


class Rectangle(Shape):
    def __init__(self, width: float, height: float) -> None:
        self.width = width
        self.height = height

    def area(self) -> float:
        return self.width * self.height

    def perimeter(self) -> float:
        return 2 * (self.width + self.height)


shapes: list[Shape] = [Circle(5), Rectangle(4, 7)]

for s in shapes:
    print(s.describe())
Output Circle: area=78.53981633974483, perimeter=31.41592653589793 Rectangle: area=28, perimeter=22

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.

Good foundation. You can now define and implement abstract methods. The next question is: can the base class provide a default that subclasses can build on rather than replace entirely? Yes — and that changes how you design shared logic.

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():

import json
from abc import ABC, abstractmethod
from typing import Any


class Serializer(ABC):
    @abstractmethod
    def serialize(self, data: dict[str, Any]) -> str:
        """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: dict[str, Any]) -> str:
        super().serialize(data)  # run the base validation
        return json.dumps(data, indent=2)


class CsvSerializer(Serializer):
    def serialize(self, data: dict[str, Any]) -> str:
        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"}))
Output { "name": "Kandi", "role": "engineer" } name,role Kandi,engineer
Pro Tip

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.

fact

The entire CPython implementation of @abstractmethod is four lines: it sets one attribute (__isabstractmethod__ = True) on the function and returns it. All enforcement logic lives in ABCMeta.__new__ and object.__new__ — the decorator itself does nothing to the function's behavior.

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) -> str:
        """Return the type of fuel this vehicle uses."""
        ...

    @property
    @abstractmethod
    def max_speed_kmh(self) -> int:
        """Return the maximum speed in km/h."""
        ...

    def summary(self) -> str:
        return f"{self.__class__.__name__}: {self.fuel_type}, max {self.max_speed_kmh} km/h"


class ElectricCar(Vehicle):
    @property
    def fuel_type(self) -> str:
        return "electric"

    @property
    def max_speed_kmh(self) -> int:
        return 200


class DieselTruck(Vehicle):
    @property
    def fuel_type(self) -> str:
        return "diesel"

    @property
    def max_speed_kmh(self) -> int:
        return 120


print(ElectricCar().summary())
print(DieselTruck().summary())
Output ElectricCar: electric, max 200 km/h DieselTruck: diesel, max 120 km/h

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. For a full comparison of how @classmethod and @staticmethod differ, see the dedicated article.

import json
from abc import ABC, abstractmethod
from typing import Any


class Parser(ABC):
    @classmethod
    @abstractmethod
    def from_string(cls, raw: str) -> "Parser":
        """Parse a raw string and return an instance."""
        ...

    @staticmethod
    @abstractmethod
    def file_extension() -> str:
        """Return the file extension this parser handles."""
        ...


class JsonParser(Parser):
    def __init__(self, data: dict[str, Any]) -> None:
        self.data = data

    @classmethod
    def from_string(cls, raw: str) -> "JsonParser":
        return cls(json.loads(raw))

    @staticmethod
    def file_extension() -> str:
        return ".json"


p = JsonParser.from_string('{"key": "value"}')
print(p.data)
print(JsonParser.file_extension())
Output {'key': 'value'} .json
Decorator Stacking Order

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) -> None:
        ...

    @abstractmethod
    def fetch(self, query: str) -> str:
        ...

    @abstractmethod
    def close(self) -> None:
        ...


class SqlDataSource(DataSource):
    """Implements connect and close, leaves fetch abstract."""
    def connect(self) -> None:
        print("SQL connection established")

    def close(self) -> None:
        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: str) -> str:
        return f"Postgres result for: {query}"


pg = PostgresSource()
pg.connect()
print(pg.fetch("SELECT * FROM users"))
pg.close()
Output Still abstract: Can't instantiate abstract class SqlDataSource without an implementation for abstract method 'fetch' SQL connection established Postgres result for: SELECT * FROM users SQL connection closed
check your understanding question 1 of 4

Hierarchy built. You have seen partial implementations, layered hierarchies, and intermediate abstract classes. Before looking inside the machinery, there is one debugging tool worth knowing: the __abstractmethods__ frozenset tells you exactly what a class still needs.

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__}")
Output Shape requires: frozenset({'area', 'perimeter'}) SqlDataSource requires: frozenset({'fetch'}) PostgresSource requires: frozenset()

An empty frozenset means all abstract methods have been implemented and the class can be instantiated.

fact

You can check any class — even built-in Python types — for __abstractmethods__. Try import collections.abc; print(collections.abc.Mapping.__abstractmethods__). You will see the exact methods any mapping type must implement: frozenset({'__getitem__', '__len__', '__iter__'}).

How It Works Under the Hood

Understanding the internal mechanism explains why certain patterns are required and helps you reason about edge cases. The @abstractmethod decorator is deceptively simple — it does exactly one thing to the function it wraps:

# This is the entire body of abstractmethod() in CPython's abc.py
def abstractmethod(funcobj):
    funcobj.__isabstractmethod__ = True
    return funcobj

That single attribute flag drives the entire enforcement machinery. Here is the chain that follows from it:

  1. 01

    @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 raises TypeError naming the missing methods.

  2. 02

    The mechanism is a single flag: @abstractmethod sets __isabstractmethod__ = True on the function. ABCMeta.__new__ collects flagged names into __abstractmethods__. object.__new__ checks that frozenset at instantiation time.

  3. 03

    Inherit from abc.ABC: The simplest way to create an abstract class is to inherit from ABC. This is equivalent to using metaclass=ABCMeta but cleaner.

  4. 04

    Abstract methods can have bodies: The base class can provide default logic. Subclasses are still required to override, but they can call super() to reuse the default. This is a deliberate Python design choice documented in PEP 3119.

  5. 05

    Stack decorators with @abstractmethod innermost: When combining with @property, @classmethod, or @staticmethod, place @abstractmethod directly above def.

  6. 06

    Intermediate abstract classes are valid: A subclass that implements only some abstract methods remains abstract and cannot be instantiated. Further subclasses must complete the remaining methods.

  7. 07

    Inspect __abstractmethods__ for debugging: This frozenset on any ABC-derived class shows which methods still need implementation. An empty frozenset means the class is concrete.

  8. 08

    ABCs can have __init__: An abstract class can define __init__ for shared setup. Subclass constructors call super().__init__() to run it. The abstract guard only prevents instantiating the ABC directly, not running its constructor code.

  9. 09

    register() bypasses @abstractmethod enforcement: Virtual subclasses satisfy isinstance() and issubclass() checks but are not subject to the abstract method guard. Use this for third-party types you cannot modify.

  10. 10

    Use update_abstractmethods() for dynamic modification (Python 3.10+): The proper way to recalculate the abstract method set after class creation. Direct assignment to __abstractmethods__ is not supported.

  11. 11

    Consider Protocol for structural typing: When you don't want to force inheritance, typing.Protocol (PEP 544, Python 3.8+) checks structure rather than lineage. ABCs enforce via inheritance; Protocols enforce via structural matching.

  12. 12

    Choose ABCs for public APIs and large teams: Use @abstractmethod when you want enforcement and discoverability. Use duck typing or Protocol when flexibility and independence from the inheritance tree matters more.

According to PEP 3119, this design was intentional: the check happens at instantiation rather than at class-definition time, so intermediate abstract classes can inherit from other abstract classes without being forced to implement everything immediately.

You can verify the flag directly on any decorated method:

from abc import ABC, abstractmethod


class Renderer(ABC):
    @abstractmethod
    def render(self) -> str:
        ...


# The flag lives on the unbound function object
print(Renderer.render.__isabstractmethod__)  # True
print(Renderer.__abstractmethods__)          # frozenset({'render'})


class HtmlRenderer(Renderer):
    def render(self) -> str:
        return ""


# Once overridden, the name is gone from the frozenset
print(HtmlRenderer.__abstractmethods__)      # frozenset()
Output True frozenset({'render'}) frozenset()
Custom Descriptors

If you build your own descriptor class and want it to participate in the abstract method system, set __isabstractmethod__ as a property that returns True when any underlying callable is abstract. Python's built-in property does exactly this: it checks __isabstractmethod__ on its getter, setter, and deleter functions.

Mechanism understood. The flag-and-frozenset chain explains every enforcement behavior you have seen. Now comes a surprising edge case: Python lets a class satisfy isinstance() checks for an ABC without inheriting from it at all.

Virtual Subclassing with register()

The @abstractmethod enforcement only applies to classes that inherit from the ABC through the normal inheritance chain. Python's ABC machinery also supports a separate concept called virtual subclassing, where a class can be registered as a subclass of an ABC without inheriting from it:

from abc import ABC, abstractmethod


class Drawable(ABC):
    @abstractmethod
    def draw(self) -> str:
        ...


# LegacyWidget does NOT inherit from Drawable
class LegacyWidget:
    def draw(self) -> str:
        return "drawing legacy widget"


# Register it as a virtual subclass
Drawable.register(LegacyWidget)

print(issubclass(LegacyWidget, Drawable))   # True
print(isinstance(LegacyWidget(), Drawable)) # True

# But abstractmethod enforcement does NOT apply to virtual subclasses.
# LegacyWidget is only registered — it can be instantiated even if it
# lacked a draw() method, because @abstractmethod only guards
# classes in the direct inheritance chain.
widget = LegacyWidget()
print(widget.draw())
Output True True drawing legacy widget

Virtual subclassing is how the collections.abc module makes built-in types like list and dict satisfy interface checks like isinstance([], collections.abc.MutableSequence) without those types inheriting from user-defined ABCs. As the CPython documentation notes, @abstractmethod only affects subclasses derived through regular inheritance — register() bypasses the enforcement entirely.

register() Does Not Enforce Abstract Methods

A class registered via Drawable.register(SomeClass) will satisfy isinstance() and issubclass() checks against Drawable, but Python will not block instantiation of SomeClass if it is missing the abstract method. The @abstractmethod guard only protects the direct inheritance path.

Dynamic Modification with update_abstractmethods()

Directly assigning to __abstractmethods__ after a class is created is not supported by the standard machinery. Python 3.10 introduced abc.update_abstractmethods() as the proper, supported way to recalculate the abstract method set for a class after its definition:

# update_abstractmethods() requires Python 3.10+
from abc import ABC, abstractmethod, update_abstractmethods


class Plugin(ABC):
    @abstractmethod
    def execute(self) -> str:
        ...


class ConcretePlugin(Plugin):
    pass  # Missing execute — still abstract


# Dynamically inject a concrete implementation
def _execute(self) -> str:
    return "executed"

ConcretePlugin.execute = _execute  # type: ignore[method-assign]

# Recalculate the frozenset to reflect the new state
update_abstractmethods(ConcretePlugin)

print(ConcretePlugin.__abstractmethods__)  # frozenset()
p = ConcretePlugin()                       # No longer raises TypeError
print(p.execute())
Output frozenset() executed

update_abstractmethods() is primarily useful when writing class decorators or metaclass tooling that adds or removes method implementations programmatically. It is available from Python 3.10 onward. In normal application code, you will rarely need it — the standard inheritance pattern handles the common cases.

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. PEP 3119 explicitly addresses this: Guido van Rossum wrote that the introduction of ABCs does not mean the end of duck typing, and that Python would not require classes to inherit from ABCs simply because they implement the same methods.

Note

ABCs and duck typing are not mutually exclusive. The register() method lets you satisfy isinstance() checks without requiring inheritance, bridging both worlds. However, remember that @abstractmethod enforcement does not apply to registered virtual subclasses — only to classes in the direct inheritance chain.


Can an Abstract Class Have __init__?

Yes — and this trips up many developers. An abstract base class can define __init__ for shared constructor logic, default attributes, or parameter validation. The abstract guard only prevents creating an instance of the ABC itself, not executing its constructor code in a subclass context.

from abc import ABC, abstractmethod


class Connection(ABC):
    def __init__(self, host: str, port: int, timeout: float = 30.0) -> None:
        self.host = host
        self.port = port
        self.timeout = timeout
        self._connected: bool = False

    @abstractmethod
    def connect(self) -> None:
        """Establish the connection."""
        ...

    @abstractmethod
    def send(self, data: bytes) -> None:
        """Send raw bytes."""
        ...

    def disconnect(self) -> None:
        """Shared teardown logic."""
        self._connected = False
        print(f"Disconnected from {self.host}:{self.port}")


class TcpConnection(Connection):
    def connect(self) -> None:
        # calls the shared __init__ automatically via normal inheritance
        print(f"TCP connect to {self.host}:{self.port} (timeout={self.timeout}s)")
        self._connected = True

    def send(self, data: bytes) -> None:
        print(f"TCP send: {len(data)} bytes")


class TlsConnection(Connection):
    def __init__(self, host: str, port: int, cert_path: str, timeout: float = 30.0) -> None:
        super().__init__(host, port, timeout)   # run shared setup
        self.cert_path = cert_path

    def connect(self) -> None:
        print(f"TLS connect using cert: {self.cert_path}")
        self._connected = True

    def send(self, data: bytes) -> None:
        print(f"TLS encrypted send: {len(data)} bytes")


tcp = TcpConnection("db.internal", 5432)
tcp.connect()
tcp.disconnect()

tls = TlsConnection("api.example.com", 443, "/certs/client.pem")
tls.connect()
tls.send(b"GET / HTTP/1.1")
Output TCP connect to db.internal:5432 (timeout=30.0s) Disconnected from db.internal:5432 TLS connect using cert: /certs/client.pem TLS encrypted send: 14 bytes

The key insight: TlsConnection.__init__ calls super().__init__() to run the shared setup from Connection, then adds its own cert_path attribute. This is standard cooperative inheritance. The ABC provides the shared structure; each concrete subclass extends or overrides as needed.

Pro Tip

Putting parameter validation and shared attribute setup in the ABC's __init__ is a good pattern — it ensures every concrete subclass shares the same baseline initialization without repeating that logic in each one.

Common Mistakes

These are the errors that appear repeatedly when developers first use @abstractmethod. Each involves a subtle misunderstanding of how the decorator and the ABC machinery interact.

Forgetting to inherit from ABC

The @abstractmethod decorator sets a flag on the function. Nothing happens unless the class inherits from ABC or uses metaclass=ABCMeta. Without it, the decorator is silently ignored and the class instantiates normally.

Broken — no enforcement
from abc import abstractmethod

class Animal:          # forgot ABC
    @abstractmethod
    def speak(self):
        pass

a = Animal()           # no error — silently broken
print(a.speak())       # returns None
Correct
from abc import ABC, abstractmethod

class Animal(ABC):     # ABC is required
    @abstractmethod
    def speak(self):
        pass

a = Animal()           # TypeError raised correctly

Wrong decorator stacking order

When combining @abstractmethod with @property, @classmethod, or @staticmethod, reversing the order makes the method concrete — Python sees the outer decorator first and the __isabstractmethod__ flag is never propagated.

Wrong order — not abstract
class Base(ABC):
    @abstractmethod   # wrong: outermost
    @property
    def name(self):
        pass
# Subclass can be created without
# implementing name — silently broken
Correct — @abstractmethod innermost
class Base(ABC):
    @property         # outermost
    @abstractmethod   # innermost — correct
    def name(self):
        pass
# Subclass must implement @property name

Using the deprecated abstractproperty

Before Python 3.3, abc.abstractproperty was the way to declare abstract properties. It was deprecated in 3.3 and should not appear in any code written after that. The correct pattern is stacking @property and @abstractmethod.

Deprecated since Python 3.3

Do not use @abc.abstractproperty, @abc.abstractclassmethod, or @abc.abstractstaticmethod. All three are deprecated. Use @property @abstractmethod, @classmethod @abstractmethod, and @staticmethod @abstractmethod instead.

isinstance() surprises with ABCs

ABCs modify how isinstance() and issubclass() behave. A class registered with ABC.register() will pass isinstance() checks even though it never inherited from the ABC and has no abstract method enforcement. This can produce unexpected results when you rely on isinstance() for type safety:

from abc import ABC, abstractmethod


class Flyable(ABC):
    @abstractmethod
    def fly(self) -> None:
        ...


class Penguin:
    # Does NOT implement fly — but we register it anyway
    pass


Flyable.register(Penguin)

p = Penguin()
print(isinstance(p, Flyable))   # True  — passes the check
print(hasattr(p, "fly"))        # False — the method doesn't exist

# Calling code that trusts isinstance() will fail:
def launch(thing: Flyable) -> None:
    thing.fly()   # AttributeError at runtime

launch(p)         # register() cannot save you here
Output True False AttributeError: 'Penguin' object has no attribute 'fly'

The takeaway: register() is a declaration of intent — you are asserting the class conforms to the ABC's interface — not a verification. Use it only when you are certain the registered class does implement the expected behavior.

spot the bug

Four lines are highlighted. One of them contains a bug that silently breaks the abstract method enforcement. Click the line you think is wrong to get feedback. You can try all of them to see every explanation.

1from abc import abstractmethod class Notifier: @abstractmethod def send(self, message): pass class EmailNotifier(Notifier): def send(self, message): print(f"Email: {message}") n = EmailNotifier() # No error? Something is wrong n.send("Hello")

Click any line you think contains the bug →

@abstractmethod vs Protocol

Python 3.8 introduced typing.Protocol (defined in PEP 544) as a structural alternative to ABCs. Where @abstractmethod enforces an interface through inheritance, Protocol checks structure — if an object has the right methods with the right signatures, it satisfies the protocol regardless of its class hierarchy.

typing.Protocol — structural subtyping

With Protocol, there is no required base class. Any class that implements the right methods is automatically compatible — no inheritance, no registration.

ABC: requires inheritance Protocol: structural matching
ABC: enforced at instantiation Protocol: enforced by static type checkers (mypy, pyright)
ABC: runtime isinstance() support Protocol: isinstance() only with runtime_checkable
from typing import Protocol, runtime_checkable


@runtime_checkable
class Drawable(Protocol):
    def draw(self) -> str: ...
    def bounding_box(self) -> tuple[float, float, float, float]: ...


# No inheritance needed — any class with these methods qualifies
class Circle:
    def draw(self) -> str:
        return "drawing circle"

    def bounding_box(self) -> tuple[float, float, float, float]:
        return (0.0, 0.0, 10.0, 10.0)


class Square:
    def draw(self) -> str:
        return "drawing square"

    def bounding_box(self) -> tuple[float, float, float, float]:
        return (0.0, 0.0, 5.0, 5.0)


# Both pass isinstance() because @runtime_checkable is set
print(isinstance(Circle(), Drawable))   # True
print(isinstance(Square(), Drawable))   # True

# A static type checker would also accept both in place of Drawable
def render(shape: Drawable) -> None:
    print(shape.draw())

render(Circle())
render(Square())
Output True True drawing circle drawing square

The practical decision between ABCs and Protocols comes down to this: if you control the class hierarchy and want runtime enforcement, use @abstractmethod. If you want to describe an interface that third-party or built-in types can satisfy without modification, and you want static type checkers to enforce it, use Protocol. The two are not mutually exclusive — a class can satisfy both an ABC and a Protocol simultaneously.

ABCs in the Python standard library

The collections.abc module defines abstract base classes for all of Python's container types. When you write code that accepts "any mapping" or "any iterable," you should type-hint against these ABCs rather than against concrete types like dict or list.

collections.abc.MutableSequence requires __getitem__, __setitem__, __delitem__, __len__, and insert. Implement those five methods and you get 14 additional methods — append, extend, reverse, pop, remove, and more — for free as mixin implementations. This is how Python's own list, bytearray, and array.array are built.

Every for loop, every in check, every call to sorted() or enumerate() runs against objects whose behavior is defined through this ABC hierarchy.

Python's io module is built entirely around ABCs. io.IOBase, io.RawIOBase, io.BufferedIOBase, and io.TextIOBase form a hierarchy of abstract classes. Every file object returned by open() is a concrete subclass of one of these.

io.RawIOBase requires subclasses to implement read() and write(). io.IOBase provides readline(), readlines(), writelines(), and iterator support as mixins on top. This is why your custom file-like object — inheriting from the right ABC and implementing the required methods — works transparently with every library that accepts a file object.

When you write def process(f: io.IOBase), you are accepting any readable stream — a real file, a socket, a BytesIO buffer, a custom subclass — without caring about its concrete type.

The numbers module (PEP 3141) defines a numerical tower of ABCs: Number, Complex, Real, Rational, and Integral, each inheriting from the previous. int, float, complex, and fractions.Fraction are all registered as virtual subclasses of this tower.

This is why isinstance(3.14, numbers.Real) returns True and isinstance(3, numbers.Integral) returns True. Library code that wants to accept "any numeric type" type-hints against numbers.Number or a more specific level of the tower, rather than enumerating int | float | complex | Decimal.

Third-party numeric types like numpy.float64 register themselves against the numbers ABCs so they satisfy these checks without modifying Python's standard library.

Key Takeaways

  1. @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 raises TypeError naming the missing methods.
  2. The mechanism is a single flag: @abstractmethod sets __isabstractmethod__ = True on the function. ABCMeta.__new__ collects flagged names into __abstractmethods__. object.__new__ checks that frozenset at instantiation time.
  3. Inherit from abc.ABC: The simplest way to create an abstract class is to inherit from ABC. This is equivalent to using metaclass=ABCMeta but cleaner.
  4. 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. This is a deliberate Python design choice documented in PEP 3119, distinguishing it from Java's abstract methods and C++ pure virtual functions.
  5. Stack decorators with @abstractmethod innermost: When combining with @property, @classmethod, or @staticmethod, place @abstractmethod directly above def.
  6. 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.
  7. Inspect __abstractmethods__ for debugging: This frozenset on any ABC-derived class shows which methods still need to be implemented. An empty frozenset means the class is concrete.
  8. register() bypasses @abstractmethod enforcement: Virtual subclasses satisfy isinstance() and issubclass() checks but are not subject to the abstract method guard. Use this for third-party types you cannot modify.
  9. Use update_abstractmethods() for dynamic modification (Python 3.10+): If you need to programmatically add or remove method implementations after class creation, use abc.update_abstractmethods() rather than directly assigning to __abstractmethods__.
  10. Choose ABCs for public APIs and large teams: Use @abstractmethod when 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. For more Python tutorials covering decorators, OOP patterns, and the standard library, the full library is available on the home page.

How to Use @abstractmethod: Step-by-Step

  1. Import from the abc module. Add from abc import ABC, abstractmethod at the top of your file. Both names are needed: ABC for the base class and abstractmethod for the decorator.
  2. Create an abstract base class. Define a class that inherits from ABC. This activates ABCMeta and enables abstract method enforcement. Example: class Shape(ABC):
  3. Decorate methods with @abstractmethod. Apply the decorator to any method a subclass must implement. The decorator sets __isabstractmethod__ = True on the function and the ABC machinery registers it as required.
  4. Create a concrete subclass. Inherit from the ABC and provide a concrete implementation for every abstract method. Once all abstract methods are overridden, the subclass can be instantiated normally.
  5. Stack decorators correctly when needed. When combining @abstractmethod with @property, @classmethod, or @staticmethod, place @abstractmethod as the innermost decorator — directly above the def keyword.
  6. Optionally add a body to the abstract method. Abstract methods can contain a default implementation. Subclasses must still override the method but can call the base logic via super().method().
  7. Inspect __abstractmethods__ for debugging. Check ClassName.__abstractmethods__ to see which methods still need to be implemented. An empty frozenset means the class is concrete and ready to instantiate.

Frequently Asked Questions

What does @abstractmethod do in Python?

The @abstractmethod decorator marks a method in an abstract base class (ABC) as required. Internally it sets __isabstractmethod__ = True on the function object. ABCMeta then collects all such names into the class-level __abstractmethods__ frozenset. Any subclass that inherits from the ABC must provide its own implementation of every abstract method before it can be instantiated. Attempting to create an instance of a class that has not overridden all abstract methods raises a TypeError naming the missing methods.

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 actual 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. PEP 3119 explicitly notes this as a deliberate design choice.

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. The older abstractproperty helper was deprecated in Python 3.3 in favor of this stacking approach.

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. ABCMeta is what actually powers the enforcement: its __new__ method computes the __abstractmethods__ frozenset at class creation time.

How does @abstractmethod work internally in Python?

The @abstractmethod decorator sets __isabstractmethod__ = True on the decorated function object. When ABCMeta.__new__ runs at class creation, it scans the class namespace and all base classes, collecting every name whose object carries __isabstractmethod__ = True into a frozenset stored as cls.__abstractmethods__. Python's object.__new__ checks this frozenset at instantiation time: if it is non-empty, a TypeError is raised. When a subclass overrides a method without @abstractmethod, that name is removed from the frozenset.

Can you dynamically add abstract methods to a class after it is created?

Yes, but only through the update_abstractmethods() function added in Python 3.10. Directly assigning to __abstractmethods__ after class creation is not supported by the standard mechanism. update_abstractmethods() recalculates the frozenset for a class, enabling dynamic modification of its abstract status after definition. This is primarily useful in class decorators and metaclass tooling.

Can an abstract base class have an __init__ method?

Yes. An ABC can define __init__ for shared constructor logic, default attributes, or parameter validation. The abstract guard only prevents instantiating the ABC directly — it does not block the constructor from running in subclasses. Concrete subclasses call super().__init__() to run the shared setup, then add their own initialization on top.

What is the difference between @abstractmethod and Protocol?

@abstractmethod enforces an interface through inheritance: a class must inherit from the ABC and override every abstract method. typing.Protocol (PEP 544, Python 3.8+) uses structural matching: any class with the right methods satisfies the Protocol, regardless of its inheritance hierarchy. ABCs enforce at runtime via TypeError; Protocols are enforced by static type checkers like mypy and pyright.

What happens if I forget to inherit from ABC when using @abstractmethod?

Nothing — the decorator is silently ignored. @abstractmethod only sets __isabstractmethod__ = True on the function. The enforcement mechanism lives in ABCMeta, which is only active when the class inherits from ABC or uses metaclass=ABCMeta. Without that, the class instantiates normally even with abstract methods defined.

The @abstractmethod decorator marks a method in an abstract base class (ABC) as required. Internally it sets __isabstractmethod__ = True on the function object. ABCMeta then collects all such names into the class-level __abstractmethods__ frozenset. Any subclass that inherits from the ABC must provide its own implementation of every abstract method before it can be instantiated. Attempting to create an instance raises a TypeError naming the missing methods.

Yes. Unlike abstract methods in Java or C++, Python's @abstractmethod allows the base class method to contain a body with actual 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. PEP 3119 explicitly notes this as a deliberate design choice.

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. The older abstractproperty helper was deprecated in Python 3.3 in favor of this stacking approach.

ABC is a helper class that uses ABCMeta as its metaclass. Inheriting from ABC is the modern, simplified way to create an abstract class. ABCMeta is what actually powers the enforcement: its __new__ method computes the __abstractmethods__ frozenset at class creation time.

@abstractmethod sets __isabstractmethod__ = True on the function. ABCMeta.__new__ collects all flagged names into __abstractmethods__. object.__new__ checks that frozenset at instantiation time and raises TypeError if it is non-empty. Overriding a method removes its name from the frozenset.

Yes, but only through update_abstractmethods() added in Python 3.10. Direct assignment to __abstractmethods__ after class creation is not supported. This function is primarily useful in class decorators and metaclass tooling.

Yes. An ABC can define __init__ for shared constructor logic. The abstract guard only prevents instantiating the ABC directly. Subclasses call super().__init__() to run the shared setup.

@abstractmethod requires inheritance from the ABC. typing.Protocol (PEP 544) uses structural matching — any class with the right methods qualifies regardless of lineage. ABCs enforce at runtime; Protocols are enforced by static type checkers.

@abstractmethod is silently ignored and the class instantiates normally. The enforcement lives in ABCMeta, which only activates when the class inherits from ABC or uses metaclass=ABCMeta.