Home/Learn/Python A–Z/Duck Typing, ABCs & Protocols

Duck Typing, ABCs & Protocols

Advanced
OOP

Python trusts behaviour over declared types — "if it quacks, it's a duck". ABCs enforce interfaces at instantiation; Protocols (3.8+) bring static duck typing for type checkers.

Overview

Java asks "what do you implement?"; Python asks "what can you do?". Duck typing means any object with the right methods works — no interface declaration required. When you DO want an enforced contract, abc.ABC + @abstractmethod prevents instantiating incomplete implementations (fails fast, like Java interfaces). typing.Protocol gives the third way: structural typing checked statically — a class satisfies a Protocol just by having the right methods, no inheritance needed. These three styles are how Python codebases scale beyond scripts.

Duck Typing & ABC Contracts

The payment example: any object with .pay() works. The ABC version guarantees subclasses implement it — instantiation fails otherwise, not first-call.

Trust the quack — or enforce it with ABC
from abc import ABC, abstractmethod

# Duck typing — no interface needed
class UPI:
    def pay(self, amt): return f"UPI paid {amt}"
class Card:
    def pay(self, amt): return f"Card paid {amt}"

def checkout(method, amount):        # accepts ANYTHING with .pay()
    return method.pay(amount)

print(checkout(UPI(), 499))          # works
print(checkout(Card(), 499))         # works

# ABC — enforced contract
class PaymentGateway(ABC):
    @abstractmethod
    def pay(self, amount): ...

    @abstractmethod
    def refund(self, txn_id): ...

class Razorpay(PaymentGateway):
    def pay(self, amount): return f"rzp txn for {amount}"
    def refund(self, txn_id): return f"refunded {txn_id}"

class Broken(PaymentGateway):
    def pay(self, amount): return "..."
    # refund missing!

Razorpay()          # fine
# Broken()          # TypeError: Can't instantiate abstract class

Protocols — Structural Typing for mypy

A Protocol declares required methods; ANY class with matching methods satisfies it — checked by the type checker, no inheritance, no registration. This is duck typing made static-analysis-friendly.

Protocol: interface by shape, not by inheritance
from typing import Protocol

class Notifier(Protocol):
    def send(self, to: str, message: str) -> bool: ...

# No inheritance from Notifier — still compatible!
class EmailNotifier:
    def send(self, to: str, message: str) -> bool:
        print(f"email to {to}: {message}")
        return True

class SMSNotifier:
    def send(self, to: str, message: str) -> bool:
        print(f"sms to {to}: {message}")
        return True

def alert_user(n: Notifier, user: str) -> None:   # typed to the SHAPE
    n.send(user, "Your interview is tomorrow!")

alert_user(EmailNotifier(), "asha@x.com")   # mypy: OK
alert_user(SMSNotifier(), "98xxxxxx")        # mypy: OK
# alert_user("hello", "x") -> mypy error: str has no send()

# EAFP vs LBYL — the duck-typing philosophy in error handling:
# try: obj.quack()               (EAFP — pythonic)
# except AttributeError: ...
# vs: if hasattr(obj, "quack"):  (LBYL)

Key Points to Remember

  • 1Duck typing: capability over declared type — the default Python style
  • 2ABC + @abstractmethod fails at INSTANTIATION time, not first call — fail fast
  • 3Protocol = structural typing: satisfied by shape, checked statically, no inheritance
  • 4EAFP (try/except) over LBYL (hasattr checks) is idiomatic Python

Interview Questions

Sign in to ask Aria
1

What is duck typing? How does it change API design vs Java interfaces?

MediumThoughtworks
2

ABC vs Protocol — enforcement time, inheritance requirements, and when to pick each.

HardGoogle
3

What are EAFP and LBYL? Which does Python favour and why?

MediumOracle

Ask Aria about Duck Typing, ABCs & Protocols

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…