Visitor Pattern in Python: Double Dispatch Demo
Demonstrates the Visitor design pattern with double dispatch so operations on Dog and Cat objects are selected at runtime without modifying their classes.
Python code
46 linesclass Animal:
def accept(self, visitor):
visitor.visit(self)
class Dog(Animal):
def speak(self):
return "Woof!"
class Cat(Animal):
def speak(self):
return "Meow!"
class SoundVisitor:
def visit(self, animal):
if isinstance(animal, Dog):
return self.visit_dog(animal)
elif isinstance(animal, Cat):
return self.visit_cat(animal)
def visit_dog(self, dog):
return f"Dog says: {dog.speak()}"
def visit_cat(self, cat):
return f"Cat says: {cat.speak()}"
class NameVisitor:
def visit(self, animal):
if isinstance(animal, Dog):
return self.visit_dog(animal)
elif isinstance(animal, Cat):
return self.visit_cat(animal)
def visit_dog(self, dog):
return "It's a dog!"
def visit_cat(self, cat):
return "It's a cat!"
if __name__ == "__main__":
animals = [Dog(), Cat()]
sound_visitor = SoundVisitor()
name_visitor = NameVisitor()
for animal in animals:
print(animal.accept(sound_visitor))
print(animal.accept(name_visitor))
Output
Dog says: Woof!
It's a dog!
Cat says: Meow!
It's a cat!
How it works
The accept method on each animal calls visitor.visit(self), passing itself. Because the actual class of self is known at runtime, Python dispatches to the correct overloaded visit_dog or visit_cat inside the visitor, mimicking double dispatch. This separates algorithms (visitors) from the object structure, making it easy to add new operations without modifying the animal classes. The isinstance checks in visit work because Python doesn't support method overloading by type, so manual dispatch is needed.
Common mistakes
- Forgetting to call `accept` on the object; directly calling `visit` loses double dispatch.
- Not handling unknown types in `visit`, causing AttributeError when new subclasses are added.
- Confusing this pattern with simple polymorphism, which would require modifying classes for each new operation.
Variations
- Use `@singledispatchmethod` on the `visit` method for cleaner type-based dispatch.
- Make `Animal` an abstract base class and require `accept` in the interface.
Real-world use cases
- Syntax tree traversals in compilers where you apply different operations to each node type.
- Adding serialization or export methods to a class hierarchy without touching each class.
- Implementing report generation where each entity type requires a different formatting logic.
Sponsored
More from OOP & classes
- Add property getter setter validation in Python easy
- Binary Tree Inorder Traversal in Python easy
- Borg pattern shared state in Python medium
- Bridge Pattern in Python: Separate Abstraction from Implementation medium
- Composable Predicates with the &, |, ~ Operators in Python medium
- Composition over Inheritance: How to Build a Wallet Account in Python easy
Keep learning
Related tutorials and quizzes for this topic.