Python Abstract Classes · Examples
Object-Oriented Python

Python Abstract Classes Examples

Short, runnable programs that show python abstract classes (abc) in action. Read the explanation, press Run, then change the code and try your own ideas. New to the topic? Start with the Python Abstract Classes (ABC) lesson.

1See what is still missing with __abstractmethods__

Every abstract class keeps a frozenset of the method names that still have no implementation. A child that fills in only part of the contract shrinks that set but stays abstract until it is empty. Sorting the names keeps the output stable.

See what is still missing with __abstractmethods__
from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass

    @abstractmethod
    def perimeter(self):
        pass

class HalfDone(Shape):
    def area(self):
        return 0

print("Shape still needs:", sorted(Shape.__abstractmethods__))
print("HalfDone still needs:", sorted(HalfDone.__abstractmethods__))
try:
    HalfDone()
except TypeError:
    print("HalfDone cannot be created yet")
Expected outputShape still needs: ['area', 'perimeter']
HalfDone still needs: ['perimeter']
HalfDone cannot be created yet

2One checkout, many payment methods

checkout() only knows the PaymentMethod contract, so it works with every child, including ones that need their own data such as a card number.

One checkout, many payment methods
from abc import ABC, abstractmethod

class PaymentMethod(ABC):
    @abstractmethod
    def pay(self, amount):
        """Return a receipt line."""

class Card(PaymentMethod):
    def __init__(self, last4):
        self.last4 = last4

    def pay(self, amount):
        return f"${amount:.2f} charged to card ending {self.last4}"

class Cash(PaymentMethod):
    def pay(self, amount):
        return f"${amount:.2f} paid in cash"

def checkout(total, method):
    return method.pay(total)

for method in [Card("4242"), Cash()]:
    print(checkout(19.5, method))
Expected output$19.50 charged to card ending 4242
$19.50 paid in cash

3Template method with an optional hook

prepare() fixes the recipe. brew() is abstract, so every drink must define it, while wants_milk() is a hook: a concrete method with a sensible default that children may override.

Template method with an optional hook
from abc import ABC, abstractmethod

class Beverage(ABC):
    def prepare(self):
        extra = " > add milk" if self.wants_milk() else ""
        return f"boil water > {self.brew()} > pour{extra}"

    @abstractmethod
    def brew(self):
        pass

    def wants_milk(self):
        return False

class Tea(Beverage):
    def brew(self):
        return "steep tea bag"

class Latte(Beverage):
    def brew(self):
        return "press espresso"

    def wants_milk(self):
        return True

print(Tea().prepare())
print(Latte().prepare())
Expected outputboil water > steep tea bag > pour
boil water > press espresso > pour > add milk

4An abstract property satisfied by a class attribute

The abstract property yearly_rate only demands that the name is replaced by something real. Checking uses a property; Savings gets away with a plain class attribute. The shared method after_one_year() works with both.

An abstract property satisfied by a class attribute
from abc import ABC, abstractmethod

class Account(ABC):
    def __init__(self, balance):
        self.balance = balance

    @property
    @abstractmethod
    def yearly_rate(self):
        """Interest rate as a fraction, such as 0.02."""

    def after_one_year(self):
        return round(self.balance * (1 + self.yearly_rate), 2)

class Savings(Account):
    yearly_rate = 0.03

class Checking(Account):
    @property
    def yearly_rate(self):
        return 0.001

print(Savings(1000).after_one_year())
print(Checking(1000).after_one_year())
Expected output1030.0
1001.0

5Fail early with an ABC, fail late with duck typing

Both Printer and BrokenWriter forgot write(). The duck-typed Printer happily becomes an object and only breaks when write() is called. The abstract BrokenWriter is stopped the moment you try to create it.

Fail early with an ABC, fail late with duck typing
from abc import ABC, abstractmethod

class Printer:
    pass

class Writer(ABC):
    @abstractmethod
    def write(self, text):
        pass

class BrokenWriter(Writer):
    pass

def save(writer, text):
    writer.write(text)

for label, make in [("duck typing", Printer), ("abstract class", BrokenWriter)]:
    try:
        obj = make()
        print(label, "-> object created")
        save(obj, "hello")
    except (AttributeError, TypeError):
        print(label, "-> failed")
Expected outputduck typing -> object created
duck typing -> failed
abstract class -> failed

6An abstract method can have code that children reuse

The body of an abstract method is never used automatically, but a child that overrides it can still call it with super(). This is handy for shared clean-up code that every child must extend.

An abstract method can have code that children reuse
from abc import ABC, abstractmethod

class Formatter(ABC):
    @abstractmethod
    def format(self, text):
        """Children must override this, and may reuse this base code."""
        return text.strip()

class EmailFormatter(Formatter):
    def format(self, text):
        cleaned = super().format(text)
        return f"Email: {cleaned}"

print(EmailFormatter().format("  Order shipped  "))
Expected outputEmail: Order shipped

Practise next

Python Abstract Classes (ABC) exercises → Back to the lesson

Python Inheritance examplesPython Polymorphism and Duck Typing examplesPython Composition: Has-A vs Is-A examples