Python
interfaces
object-oriented programming
programming tutorials
software development

How do I implement interfaces in python?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

Python does not have a dedicated interface keyword, but it still supports interface-style design. In practice, you choose between abstract base classes, structural typing with protocols, and plain duck typing depending on whether you need runtime checks, static analysis, or minimal ceremony.

Using Abstract Base Classes

The closest built-in equivalent to an interface is an abstract base class from the abc module. It lets you define required methods and prevents incomplete subclasses from being instantiated.

python
1from abc import ABC, abstractmethod
2
3
4class PaymentProcessor(ABC):
5    @abstractmethod
6    def charge(self, amount: float) -> str:
7        """Process a payment and return a confirmation string."""
8
9
10class StripeProcessor(PaymentProcessor):
11    def charge(self, amount: float) -> str:
12        return f"charged ${amount:.2f} with Stripe"
13
14
15processor = StripeProcessor()
16print(processor.charge(19.99))

If a subclass forgets to implement charge, Python raises a TypeError when you try to create an instance. That makes abstract base classes useful when you want an explicit runtime contract.

Abstract base classes can also contain shared behavior, which is something many developers miss when they think in terms of Java-style interfaces.

python
1from abc import ABC, abstractmethod
2
3
4class Serializer(ABC):
5    @abstractmethod
6    def dumps(self, value) -> str:
7        """Convert a value to a string."""
8
9    def write_line(self, value) -> None:
10        print(self.dumps(value))
11
12
13class CsvSerializer(Serializer):
14    def dumps(self, value) -> str:
15        return ",".join(str(item) for item in value)
16
17
18CsvSerializer().write_line([1, 2, 3])

That pattern works well when every implementation must expose the same operations and you want some base logic in one place.

Using Protocols for Structural Typing

If your goal is type checking rather than runtime enforcement, typing.Protocol is often a better fit. A protocol says, in effect, "anything with these methods is acceptable."

python
1from typing import Protocol
2
3
4class SupportsSave(Protocol):
5    def save(self) -> None:
6        ...
7
8
9class FileStore:
10    def save(self) -> None:
11        print("saved to file")
12
13
14class MemoryStore:
15    def save(self) -> None:
16        print("saved in memory")
17
18
19def persist(store: SupportsSave) -> None:
20    store.save()
21
22
23persist(FileStore())
24persist(MemoryStore())

Neither class needs to inherit from SupportsSave. A static type checker such as mypy accepts both because they satisfy the required method signature. This is called structural typing, and it matches Python's "if it behaves correctly, use it" philosophy.

Protocols are especially helpful in libraries and larger codebases where you want strong editor support and type checking without forcing inheritance chains. They also make testing easier because a small fake object can satisfy the same contract without subclassing a framework-specific base class.

If you need runtime checks with protocols, use @runtime_checkable, but do that sparingly. In many cases, the value of a protocol is static analysis rather than isinstance checks.

Duck Typing Still Matters

Not every collaboration in Python needs a formal interface. Duck typing means you simply call the methods you expect and let tests or normal exceptions reveal mistakes.

python
1class ConsoleLogger:
2    def log(self, message: str) -> None:
3        print(message)
4
5
6def run_job(logger) -> None:
7    logger.log("job started")
8
9
10run_job(ConsoleLogger())

This style is common and idiomatic, especially for short-lived internal code. It keeps the implementation small and avoids introducing types that do not add much value.

The tradeoff is that the contract is informal. When the expected methods are not obvious, future readers have to infer them from call sites or documentation. That is usually acceptable for small modules, but less so for reusable APIs or plugin systems.

Choosing the Right Tool

A useful rule is to match the mechanism to the level of coordination your code needs.

Use an abstract base class when:

  • you want runtime enforcement,
  • you want a shared parent type,
  • you want common implementation code.

Use a protocol when:

  • you use static type checking,
  • inheritance would be artificial,
  • you want flexible structural compatibility.

Use duck typing when:

  • the collaboration is simple,
  • the code is local and easy to test,
  • a formal contract would add more noise than clarity.

A plugin architecture often benefits from abstract base classes because failures happen early and clearly. A typed service layer often benefits from protocols because different implementations can remain independent. A tiny utility module usually does fine with plain duck typing.

Common Pitfalls

  • Treating abstract base classes as mandatory for all object-oriented Python code. They are useful, but they are not the default answer to every design problem.
  • Writing interfaces with too many unrelated methods. A small, focused contract is easier to implement and easier to understand.
  • Using protocols and then expecting Python itself to enforce them at runtime. Protocols are primarily for type checkers unless you explicitly add runtime support.
  • Replacing clear duck-typed code with layers of abstract types that do not improve readability or testing.
  • Using inheritance only to get a type name. If the base class adds no behavior and runtime enforcement is not needed, a protocol may be cleaner.

Summary

  • Python supports interface-style design through abstract base classes, protocols, and duck typing.
  • Abstract base classes are best when you need runtime enforcement and possibly shared behavior.
  • Protocols are best when you want structural typing and static analysis without forced inheritance.
  • Duck typing is still a good choice for simple, local collaborations.
  • Choose the smallest mechanism that makes the contract clear for the people maintaining the code.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.