Skip to content

subtyping

Subtyping is a relation between data types in which any value of the subtype can be used in any context that expects a value of the related supertype.

The relation, also called subtype polymorphism or inclusion polymorphism, is formalized by the Liskov substitution principle: code written for the supertype must keep working when given a subtype instance.

Subtyping is distinct from inheritance, since a class can inherit code without being substitutable, and unrelated classes can become subtypes through their structure alone.

Common forms of subtyping include the following:

  • Nominal subtyping, where the relation is declared explicitly through inheritance or interface implementation, as in Java or C++.
  • Structural subtyping, where the relation follows from the members a type exposes. Python supports this through Protocol classes in the typing module.
  • Duck typing, the dynamic counterpart of structural subtyping, where compatibility is checked at call time by attempting the operation.
  • Behavioral subtyping, which adds constraints on observable behavior, such as preconditions, postconditions, and invariants.

Subtyping interacts with type constructors through variance: parameter types are contravariant, return types are covariant, and generic containers are invariant by default.

Tutorial

Python Protocols: Leveraging Structural Subtyping

In this tutorial, you'll learn about Python's protocols and how they can help you get the most out of using Python's type hint system and static type checkers.

intermediate python

For additional information on related topics, take a look at the following resources:


By Martin Breuss • Updated June 10, 2026 • Reviewed by Leodanis Pozo Ramos