Duck typing in Python: what an object does matters more than what it is

Duck typing judges an object by the methods it carries rather than by its class: Python checks at call time, never before.
5 min read
Believemy logo

Definition

You are writing a function that has to make an object speak. Which objects is it allowed to receive? Many languages demand an answer written in advance: a precise class, or an interface to implement. Python answers differently, and its answer has a name: duck typing.

The rule is that Python judges an object on what it can do, not on the class it descends from. The original phrasing says it better than a definition: if an object walks like a duck and quacks like a duck, treat it as a duck. No declaration up front, no interface to sign: the method being called only has to exist on the object at the moment it is called.

Two classes with no family tie whatsoever, and one function that treats them exactly alike:

PYTHON
class Duck:
    def quack(self):
        return "quack"

class Robot:
    def quack(self):
        return "synthetic quack"

def make_it_speak(animal):
    # No check at all: we call, and we will see.
    print(animal.quack())

make_it_speak(Duck())
make_it_speak(Robot())

The make_it_speak function never asks for the type of what it receives: it calls, and Python looks the name quack up on the object at the very last moment. An object written elsewhere, by somebody who had never heard of that function, will therefore work with it without a single line of adaptation: compatibility is not declared, it is observed.


What Python checks, and when

If nothing is checked in advance, what is checked, and when? Many languages verify compatibility before execution, on a hierarchy declared in advance; Python verifies it during execution, on the names actually present. The table puts them face to face, Java or C# on the left, Python on the right.

Nominal typingDuck typing
The question asked is "what type is this object"The question asked is "does this object have what it takes"
The check happens before the program runsThe check happens on the line that calls
You must inherit or implement an interfaceYou only need to carry the right method name
A mismatch blocks compilationA mismatch raises a TypeError

Moving the check has one very concrete consequence: a class does not need to know the classes that will use it, nor the other way round. Two libraries built separately plug into each other as long as they agree on method names, with no shared dependency to install.


Protocols, the contracts nobody signs

Duck typing is not a tolerance granted to your code: Python applies it to itself first. Its own constructs do not demand a precise type, they demand a magic method, that is, a method whose name is wrapped in double underscores. Here are the four you meet earliest.

What you want to doThe method to write
Pass the object to len__len__
Walk it with for__iter__, which makes it an iterable
Open it inside a with block__enter__ and __exit__
Print it readably__str__

Nothing to inherit, nothing to declare: writing the method is enough. It is why a function that accepts "a file" really accepts any object carrying the read and write methods, and why a fake file held in memory replaces the real one in tests, without a line of the tested code moving.

Warning

Two unrelated classes can carry the same method with a different meaning. An object holding a send method does not necessarily send a message: Python will accept it without blinking and the problem will surface much further away, in a shape that no longer looks like a type error.


The price to pay: the error arrives late

That freedom has a counterpart. Nothing warns that an object is unfit before the line that uses it: missing the expected method, it travels quietly through the intermediate calls and triggers an AttributeError deep in the stack, far from where it was built. Hand a cat to the function from the beginning, and the error message will mention neither the cat nor the function.

PYTHON
class Cat:
    def meow(self):
        return "meow"

make_it_speak(Cat())

# Nothing flinches until animal.quack() is called
# AttributeError: 'Cat' object has no attribute 'quack'

Two habits limit the damage. The first is to document the expectation instead of leaving it to be guessed: a type hint or a docstring naming the required methods makes the problem show up in the editor, before the program runs.

The second is to wrap the call in a try when receiving a loosely shaped object is one of the expected cases. Interrogating the object before using it looks safer, but that caution ends up turning away objects that would have worked perfectly well.


Frequently asked questions

Question

Should the type be checked with isinstance before calling?

Rarely. An isinstance closes the door duck typing has just opened: it turns down a perfectly compatible object for the sole reason that it does not belong to the right family. Python culture prefers attempting the call and catching the failure. The check keeps its place where data enters the program from outside.

Question

Does duck typing make type hints useless?

No, the two go together. The typing module offers Protocol, which describes the expected methods without forcing any inheritance: the checker gains a guarantee and the caller keeps the freedom to supply any conforming object. A tool such as mypy reads those protocols and flags the incompatible object before the program even runs.

Question

What is the difference with inheritance?

Inheritance shares code and creates a family: a subclass receives the methods of its parent class. Duck typing shares nothing but a vocabulary: two objects with nothing in common become interchangeable because they answer to the same method name. The Python course shows where the line falls between the two.

Related terms

Discover our python glossary

Browse the terms and definitions most commonly used in development with Python.

Share this article

Want to help us? Share this article on your networks or even better: on your site, in an article or in your newsletter.