Classes

Abstract Base Classes

ABC and abstractmethod enforce that subclasses implement required methods.

ABC plus @abstractmethod declares the contract. Trying to construct the base class itself fails because at least one method has no implementation. A concrete describe() lives alongside the abstract area() so subclasses inherit shared behavior for free.

Source

from abc import ABC, abstractmethod

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

    def describe(self) -> str:
        return f"shape with area {self.area()}"

try:
    Shape()
except TypeError as error:
    print(error)

Output

Can't instantiate abstract class Shape without an implementation for abstract method 'area'
instanceCLASSClassTYPEtype
An ABC sits on the same triangle as concrete classes; subclasses inherit the abstract methods they must implement.

A subclass that implements every abstract method is concrete and can be instantiated. It also inherits the non-abstract methods from the base class.

Source

class Square(Shape):
    def __init__(self, side):
        self.side = side

    def area(self):
        return self.side ** 2

print(Square(3).area())
print(Square(3).describe())

Output

9
shape with area 9

A subclass that forgets to implement an abstract method also cannot be instantiated — that is the value the ABC adds. The error fires at construction, not when something later tries to call the missing method.

Source

class Incomplete(Shape):
    pass

try:
    Incomplete()
except TypeError as error:
    print(error)

Output

Can't instantiate abstract class Incomplete without an implementation for abstract method 'area'

Contrast with Protocol. A HasArea protocol accepts any class with an area() method, no inheritance required. Triangle does not inherit from Shape, so it satisfies the protocol but fails isinstance(_, Shape). Square satisfies both because it explicitly inherited from the ABC.

Source

from typing import Protocol

class HasArea(Protocol):
    def area(self) -> float:
        ...

class Triangle:
    def __init__(self, base, height):
        self.base = base
        self.height = height

    def area(self):
        return 0.5 * self.base * self.height

def total_area(shapes: list[HasArea]) -> float:
    return sum(shape.area() for shape in shapes)

print(total_area([Square(3), Triangle(4, 3)]))
print(isinstance(Triangle(4, 3), Shape))
print(isinstance(Square(3), Shape))

Output

15.0
False
True

Notes

See also

Run the complete example

Example code

Expected output

Can't instantiate abstract class Shape without an implementation for abstract method 'area'
9
shape with area 9
Can't instantiate abstract class Incomplete without an implementation for abstract method 'area'
15.0
False
True

Execution time appears here after you run the example.