alex_ber
4 min readJun 21, 2024

Structured Polymorphism (Duck Typing)

Duck typing is a form of polymorphism where the type of an object is determined by its behavior rather than its explicit inheritance from a particular class.

Polymorphism allows objects of different types to be treated as objects of a common super type. This is typically achieved through inheritance, where different classes inherit from a common base class or implement a common interface. The key idea is that the specific type of the object doesn’t matter as long as it adheres to the expected interface.

I’m not going to explain ad-hoc polymorphism and universal polymorphism. You can read about them elsewhere. I will do mention overriding that is part of ad-hoc polymorphism. In Python interface can have default implementation, so it is closer to mixin, you can read here for more details.

Interfaces define a set of methods and properties that a class must implement. They are a way to specify what an object can do, or what his contract is, without dictating how it should do it. Interfaces are crucial for achieving polymorphism, which is the ability of different types to be used interchangeably based on a common interface.

Example of Duck Typing in Python:

class Duck:
def quack(self):
print("Quack!")
class Person:
def quack(self):
print("I'm quacking like a duck!")
def make_it_quack(duck):
duck.quack()
d = Duck()
p = Person()
make_it_quack(d) # Output: Quack!
make_it_quack(p) # Output: I'm quacking like a duck!

In this example, Quackable is an abstract base class (interface) that defines a quack() method. Both Duck and Person classes implement this interface. The make_it_quack() function can accept any object that implements the Quackable interface, demonstrating polymorphism.

Structured Polymorphism (Duck Typing)

Duck typing is a form of polymorphism where the type or class of an object is determined by its behavior (methods and properties) rather than its explicit inheritance from a particular class. The name comes from the saying:

“If it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck.

In duck typing, if an object implements the necessary methods and properties, it can be used in place of another object, regardless of their actual types. This allows for a more flexible form of polymorphism.

Example of Duck Typing in Python:

from typing import Protocol

class Quackable(Protocol):
def quack(self) -> None:
...

class Duck:
def quack(self):
print("Quack!")

class Person:
def quack(self):
print("I'm quacking like a duck!")

def make_it_quack(duck: Quackable):
duck.quack()

d = Duck()
p = Person()

make_it_quack(d) # Output: Quack!
make_it_quack(p) # Output: I'm quacking like a duck!

In this example, both Duck and Person classes have a quack() method. The make_it_quack() function can accept an instance of either class because it relies on the presence of the quack method, not the specific type of the object.

Protocols and Duck Typing

In Python, the Protocol class from the typing module provides a way to define structural subtyping, which is a formalization of duck typing. A Protocol defines a set of methods and properties that a class must implement to be considered a subtype of the protocol, without requiring explicit inheritance.

Example of Protocol in Python:

from typing import Protocol

class Quackable(Protocol):
def quack(self) -> None:
...
class Duck:
def quack(self):
print("Quack!")
class Person:
def quack(self):
print("I'm quacking like a duck!")
def make_it_quack(duck: Quackable):
duck.quack()
d = Duck()
p = Person()
make_it_quack(d) # Output: Quack!
make_it_quack(p) # Output: I'm quacking like a duck!

In this example, Quackable is a protocol that specifies that any class implementing it must have a quack() method. Both Duck and Person classes conform to this protocol because they implement the quack() method. The make_it_quack() function can now explicitly state that it expects a Quackable object, providing better type checking and clarity.

How Protocols Relate to Duck Typing

Protocols formalize the concept of duck typing by allowing you to define expected behaviors (methods and properties) that a type must implement. This provides several benefits:

  1. Type Checking: Protocols enable static type checkers like mypy to verify that objects conform to the expected interface, catching errors at compile time rather than runtime. Side note: In Python adherence to interface is checked in runtime.
  2. Documentation: Protocols serve as a form of documentation, making it clear what methods and properties an object must implement to be used in a particular context.
  3. Flexibility: Like duck typing, protocols allow for flexible and interchangeable use of different types, as long as they adhere to the defined interface.