classDog:defspeak(self):return"Woof!"classCat:defspeak(self):return"Meow!"defanimal_sound(animal):return animal.speak()# No type checking, relies on method existenceprint(animal_sound(Dog()))# ✅ Output: Woof!print(animal_sound(Cat()))# ✅ Output: Meow!
✅ Fix: Python doesn’t check Dog or Cat type, only if speak() exists.
🔹 2. Duck Typing Fails Without Required Methods
classFish:defswim(self):return"I swim"print(animal_sound(Fish()))# ❌ AttributeError: 'Fish' object has no attribute 'speak'
❌ Issue:Fish has swim(), but no speak(), so it crashes.
✅ Fix: Use hasattr(animal, "speak") before calling.
🔹 3. Structural Typing with Protocol (Python 3.8+)
✅ Fix:Protocol enforces speak() presence at runtime.
🔹 4. Runtime Check for Structural Typing
✅ Fix: Check compliance before callingspeak().
🔹 5. Multiple Method Requirements in Protocol
✅ Fix: Enforces multiple method contracts.
🔹 6. Duck Typing with try-except to Handle Missing Methods
✅ Fix: Catch missing attributes dynamically.
🔹 7. Structural Typing for Function Arguments
✅ Fix: Ensures draw() method exists before calling.
🔹 8. Combining Duck Typing & Type Hints
✅ Fix: Duck typing with hasattr().
🔹 9. Structural Typing with @staticmethod
✅ Fix:@staticmethod inside Protocol.
🔹 10. Mixing Duck Typing & Structural Typing
✅ Fix: Duck typing (hasattr()) vs. Structural typing (Protocol).
🚀 Summary: Duck Typing vs. Structural Typing
Feature
Duck Typing
Structural Typing (Protocol)
Definition
Checks if an object has required methods at runtime.
Defines expected method names at type-check time.
Flexibility
High (any object can pass if it has required methods).
Stricter (only objects implementing the interface are allowed).
from typing import Protocol
class Drawable(Protocol):
def draw(self) -> None:
pass
class Circle:
def draw(self):
print("Drawing a circle")
class Square:
def draw(self):
print("Drawing a square")
def render(shape: Drawable):
shape.draw()
render(Circle()) # ✅ Output: Drawing a circle
render(Square()) # ✅ Output: Drawing a square
def quack(obj: object):
if hasattr(obj, "quack"):
return obj.quack()
raise TypeError("Object does not have 'quack' method")
class Duck:
def quack(self):
return "Quack!"
print(quack(Duck())) # ✅ Output: Quack!
from typing import Protocol
class Logger(Protocol):
@staticmethod
def log(message: str) -> None:
pass
class ConsoleLogger:
@staticmethod
def log(message: str):
print(f"Log: {message}")
def write_log(logger: Logger):
logger.log("Test message")
write_log(ConsoleLogger()) # ✅ Output: Log: Test message
from typing import Protocol
class Flyable(Protocol):
def fly(self) -> str:
pass
class Airplane:
def fly(self):
return "Flying in the sky"
class Bird:
def fly(self):
return "Flapping wings"
class Car:
def drive(self):
return "Driving on the road"
def take_off(obj):
if hasattr(obj, "fly"):
return obj.fly()
raise TypeError("Object can't fly!")
def take_off_strict(obj: Flyable):
return obj.fly() # Ensures fly() exists
print(take_off(Airplane())) # ✅ Output: Flying in the sky
print(take_off_strict(Bird())) # ✅ Output: Flapping wings
print(take_off(Car())) # ❌ TypeError: Object can't fly!