python abstractmethod: Defining Abstract Base Classes
Learn how python abstractmethod enforces method implementation in abstract base classes, with syntax, examples, and common pitfalls.
python abstractmethod requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.
In Python, abstractmethod is a decorator from the abc module that marks a method as required but not implemented in the base class. When a class inherits from ABC and contains at least one @abstractmethod, Python prevents the class from being instantiated directly. Subclasses must override every abstract method before they can be instantiated. This mechanism gives you a way to define a contract for a family of classes without providing a default implementation.
Declaring an Abstract Method with @abstractmethod
The most common usage is to create a base class that inherits from abc.ABC and decorate one or more methods with @abstractmethod. Here is the minimal syntax:
from abc import ABC, abstractmethod class Shape(ABC): @abstractmethod def area(self) -> float: """Return the area of the shape.""" pass
The method body can contain a pass statement or a default implementation. The decorator sets an internal flag __isabstractmethod__ on the method, which ABCMeta uses to track which methods must be overridden. If you do not inherit from ABC but still use @abstractmethod, the flag is set but the class is not automatically prevented from instantiation because the metaclass is not ABCMeta. To get the full behavior, inherit from ABC or explicitly use metaclass=ABCMeta.
How Abstract Methods Enforce Implementation at Runtime
The enforcement happens when you try to instantiate a class that has any abstract methods. Consider this subclass that does not implement area:
class Circle(Shape): def __init__(self, radius: float): self.radius = radius # Attempting to instantiate raises TypeError circle = Circle(5.0)
Running this code raises TypeError: Can't instantiate abstract class Circle with abstract method area. The ABCMeta metaclass checks all methods marked with __isabstractmethod__ during class construction. If any remain unoverridden, instantiation fails immediately. This check happens at object creation time, not at method lookup time, so the error surfaces as soon as you call the constructor.
Because the check is dynamic, you can create a subclass that implements the method and then instantiate it without issue:
class Square(Shape): def __init__(self, side: float): self.side = side def area(self) -> float: return self.side ** 2 square = Square(4.0) print(square.area()) # 16.0
The base class can also provide a partial implementation that subclasses can call via super(). For example, an abstract method might set up common state:
class Document(ABC): @abstractmethod def load(self, path: str) -> None: self.path = path class PDFDocument(Document): def load(self, path: str) -> None: super().load(path) self.format = "pdf"
Here, PDFDocument overrides load and calls super().load(path) to reuse the base logic. The abstract method still must be overridden, but the base implementation is available for subclasses that want to extend it.
Abstract Properties, Class Methods, and Static Methods
The @abstractmethod decorator can be combined with other decorators to enforce contracts on properties and class-level methods. For a property, apply @property before @abstractmethod:
class Animal(ABC): @property @abstractmethod def sound(self) -> str: pass class Dog(Animal): @property def sound(self) -> str: return "Woof"
If a subclass does not override the sound property, instantiation raises a TypeError just like with a regular method. The order of decorators matters: @property must be the outermost decorator so that abstractmethod is applied to the property object.
For class methods and static methods, the pattern is similar:
class Factory(ABC): @classmethod @abstractmethod def create(cls) -> "Factory": pass @staticmethod @abstractmethod def validate(value: int) -> bool: pass
In both cases, the @abstractmethod decorator must be placed after the @classmethod or @staticmethod decorator. This ordering ensures that the abstract flag is set on the underlying callable, not on the descriptor wrapper.
A Practical Example: Strategy Pattern with Abstract Base Classes
Abstract methods shine when you need to define a family of interchangeable algorithms. Consider a payment processor that supports multiple providers. You can define a common interface using abstractmethod:
from abc import ABC, abstractmethod class PaymentGateway(ABC): @abstractmethod def charge(self, amount: float) -> str: """Charge the given amount and return a transaction ID.""" @abstractmethod def refund(self, transaction_id: str) -> bool: """Refund a previous transaction.""" class StripeGateway(PaymentGateway): def charge(self, amount: float) -> str: # Real Stripe integration would go here return f"stripe-{amount}" def refund(self, transaction_id: str) -> bool: return transaction_id.startswith("stripe-") class PayPalGateway(PaymentGateway): def charge(self, amount: float) -> str: return f"paypal-{amount}" def refund(self, transaction_id: str) -> bool: return transaction_id.startswith("paypal-")
Each concrete gateway implements the two abstract methods. The rest of your application can depend on the PaymentGateway type without knowing which provider is in use. If a new provider is added, the compiler (or runtime check) ensures that both methods are implemented before the class can be used.
Common Mistakes When Using abstractmethod
One frequent mistake is forgetting to inherit from ABC. Without ABC or metaclass=ABCMeta, the @abstractmethod decorator still marks the method, but the class can be instantiated even if the method is not overridden. For example:
from abc import abstractmethod class Shape: # Missing ABC inheritance @abstractmethod def area(self): pass shape = Shape() # No TypeError raised
This happens because the default metaclass type does not check __isabstractmethod__. Always inherit from ABC or set metaclass=ABCMeta explicitly.
Another mistake is assuming that abstract methods cannot have a body. They can, but the subclass must still override them to be instantiable. The body is available via super() if the subclass chooses to call it.
A third issue arises when using abstract methods with multiple inheritance. If a class inherits from two abstract bases that declare the same method, the subclass must provide an override that satisfies both. If one base provides a concrete implementation and the other declares it abstract, the subclass must still override it because the abstract flag remains set.
Runtime Behavior and Instantiation Checks
The enforcement mechanism is entirely runtime. There is no compile-time verification because Python is dynamically typed. The ABCMeta metaclass performs the check during class creation, specifically in __new__. When you define a subclass, the metaclass collects all abstract methods from the base classes and checks whether the subclass overrides them. If any are missing, the class object is marked as abstract, and any attempt to instantiate it raises TypeError.
This runtime check adds a small overhead only at class definition time, not at method call time. The __isabstractmethod__ attribute is set on the method object, and the metaclass iterates over the class's __abstractmethods__ set. For typical applications, this overhead is negligible. The main performance consideration is that you cannot avoid the check by using __new__ tricks; the metaclass runs before your class's __init__.
Because the check is dynamic, you can also use abstractmethod with dynamically created classes. For example, type can be used to build a class with an abstract method, and the same rules apply. This flexibility is useful for frameworks that generate classes at runtime, but it also means that errors may surface later than they would in a statically typed language.
When Not to Use abstractmethod
abstractmethod is not always the right tool. If you only need to define a common interface and do not care about preventing instantiation, a regular base class with methods that raise NotImplementedError may be simpler. However, that approach defers the error to method call time, not instantiation time. The abstractmethod approach gives you earlier feedback.
Another alternative is duck typing: rely on the presence of methods without a formal base class. This is common in Python's dynamic style, but it loses the explicit contract and the ability to check for completeness at instantiation. Use abstractmethod when you want a formal, enforceable interface that all subclasses must satisfy.
There is also the abc.ABC convenience class, which is just a helper that sets metaclass=ABCMeta. You can use ABCMeta directly if you need to combine it with other metaclasses, but ABC is the idiomatic choice for most cases.
Finally, be aware that abstractmethod works with __init__ and __new__ as well. If you mark __init__ as abstract, subclasses must override it, but they can still call super().__init__(). This pattern is useful when you want to force subclasses to provide a custom initialization path.