Duck Typing, ABCs & Protocols
AdvancedPython 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.
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 classProtocols — 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.
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 AriaWhat is duck typing? How does it change API design vs Java interfaces?
ABC vs Protocol — enforcement time, inheritance requirements, and when to pick each.
What are EAFP and LBYL? Which does Python favour and why?
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.