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 Classfoundation
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()}"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 Forgetsfoundation
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 Methodfoundation
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())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 Bodyfoundation
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"}))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.
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 Propertiesfoundation
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())Combining with @classmethod and @staticmethodadvanced
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())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 Methodsfoundation
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()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__internals
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.
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 Hoodinternals
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 funcobjThat single attribute flag drives the entire enforcement machinery. Here is the chain that follows from it:
- 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 raisesTypeErrornaming the missing methods. - 02
The mechanism is a single flag:
@abstractmethodsets__isabstractmethod__ = Trueon the function.ABCMeta.__new__collects flagged names into__abstractmethods__.object.__new__checks that frozenset at instantiation time. - 03
Inherit from abc.ABC: The simplest way to create an abstract class is to inherit from
ABC. This is equivalent to usingmetaclass=ABCMetabut cleaner. - 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. - 05
Stack decorators with @abstractmethod innermost: When combining with
@property,@classmethod, or@staticmethod, place@abstractmethoddirectly abovedef. - 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.
- 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.
- 08
ABCs can have __init__: An abstract class can define
__init__for shared setup. Subclass constructors callsuper().__init__()to run it. The abstract guard only prevents instantiating the ABC directly, not running its constructor code. - 09
register() bypasses @abstractmethod enforcement: Virtual subclasses satisfy
isinstance()andissubclass()checks but are not subject to the abstract method guard. Use this for third-party types you cannot modify. - 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
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
Choose ABCs for public APIs and large teams: Use
@abstractmethodwhen 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()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()advanced
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())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.
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()advanced
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())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 Typingadvanced
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.
__abstractmethods__ shows what is requiredABCs 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__?foundation
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")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.
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 Mistakesgotcha
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.
from abc import abstractmethod
class Animal: # forgot ABC
@abstractmethod
def speak(self):
pass
a = Animal() # no error — silently broken
print(a.speak()) # returns None
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.
class Base(ABC):
@abstractmethod # wrong: outermost
@property
def name(self):
pass
# Subclass can be created without
# implementing name — silently broken
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.
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 hereThe 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.
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.
Click any line you think contains the bug →
@abstractmethod vs Protocoladvanced
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.
With Protocol, there is no required base class. Any class that implements the right methods is automatically compatible — no inheritance, no registration.
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())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.
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
- @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. - The mechanism is a single flag:
@abstractmethodsets__isabstractmethod__ = Trueon the function.ABCMeta.__new__collects flagged names into__abstractmethods__.object.__new__checks that frozenset at instantiation time. - 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. This is a deliberate Python design choice documented in PEP 3119, distinguishing it from Java's abstract methods and C++ pure virtual functions. - 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. An empty frozenset means the class is concrete.
- register() bypasses @abstractmethod enforcement: Virtual subclasses satisfy
isinstance()andissubclass()checks but are not subject to the abstract method guard. Use this for third-party types you cannot modify. - 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__. - 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. 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
- Import from the abc module. Add
from abc import ABC, abstractmethodat the top of your file. Both names are needed:ABCfor the base class andabstractmethodfor the decorator. - Create an abstract base class. Define a class that inherits from
ABC. This activatesABCMetaand enables abstract method enforcement. Example:class Shape(ABC): - Decorate methods with @abstractmethod. Apply the decorator to any method a subclass must implement. The decorator sets
__isabstractmethod__ = Trueon the function and the ABC machinery registers it as required. - 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.
- Stack decorators correctly when needed. When combining
@abstractmethodwith@property,@classmethod, or@staticmethod, place@abstractmethodas the innermost decorator — directly above thedefkeyword. - 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(). - 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.