[Python] About Protocol in PEP 544
→ 日本語版を読むThis article was written using AI.
[Python] PEP 544 (Protocol) Explained for Beginners — How to Use Duck Typing with Type Hints
While studying Python type hints, you may have come across the terms "PEP 544" or "Protocol".
In short, PEP 544 is "a mechanism that allows Python's inherent flexibility of 'duck typing' to be used safely with static type checking."
In this article, I'll explain the background behind the introduction of PEP 544 and how to use it in a clear and accessible way.
1. What Is "Duck Typing"?
Python has long had a culture of duck typing.
"If it walks like a duck and quacks like a duck, then it must be a duck."
In other words, what matters is not the "actual class (name)" of an object, but whether it "has the required methods and attributes (what it can do)."
2. The Problem with Previous Type Hints
However, the "type hints" introduced in Python 3.5 were fundamentally based on "Nominal Subtyping." This is a mechanism that checks types based on the "name (inheritance relationship)" of the class.
For example, consider the following code.
class Dog:
def speak(self) -> None:
print("Woof!")
# Only accepts Dog type (or a class that inherits from Dog)
def make_it_speak(animal: Dog):
animal.speak()
Now suppose you create a Cat class with the exact same speak() method.
class Cat:
def speak(self) -> None:
print("Meow!")
cat = Cat()
make_it_speak(cat) # Works at runtime, but causes a type check error (e.g., with mypy)!
It runs fine in Python's runtime environment (duck typing). However, type checking tools (such as mypy or Pyright) will complain that "a Dog type is required, but a Cat type was passed."
Creating a common parent class just to solve this and having classes inherit from it undermines Python's flexibility and is inconvenient.
3. PEP 544's Solution: "Structural Subtyping" and Protocol
To solve this problem, PEP 544 introduced "Structural Subtyping." Instead of checking based on the class name, it checks types based on the class's "structure (the methods it has)."
The tool used to achieve this is typing.Protocol.
Concrete Code Example
from typing import Protocol
# 1. Define a rule (protocol) that says "has a speak method"
class Speaker(Protocol):
def speak(self) -> None:
... # body is omitted (pass)
# 2. Create classes normally (no explicit inheritance needed!)
class Dog:
def speak(self) -> None:
print("Woof!")
class Cat:
def speak(self) -> None:
print("Meow!")
# 3. Specify the "Protocol" as the argument type
def make_it_speak(animal: Speaker):
animal.speak()
# Either can be passed and will pass type checking!
make_it_speak(Dog())
make_it_speak(Cat())
What's Happening?
- We defined a "rule (interface)" called
Speaker. - Neither
DognorCatinherits fromSpeaker. - However, since both have the
speak()method, they are automatically recognized as a kind ofSpeakertype.
4. Benefits of Using PEP 544 (Protocol)
- No explicit inheritance (import) required Even for classes from third-party libraries or the standard library, as long as they have the required methods, they will pass type checking.
- Reduced code coupling Rather than "depending on this class," you can design code to "depend on an object with this capability (method)," resulting in cleaner code.
- Both safety and flexibility You get the best of both worlds — the freedom and flexibility of Python's duck typing, along with the full benefits of static type checking (early bug detection, editor autocompletion).
Summary
- PEP 544 is the specification that introduced
Protocolto Python. - Instead of inheritance relationships, it checks types based on the methods an object has (its structure).
- It's a powerful feature that enables Python's inherent "duck typing" to be used safely in the world of type hints.
It's essential knowledge for modern Python development (especially large-scale development and robust library development), so give it a try!