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.

Medium Python 3.9+ Aug 9, 2026 OOP & classes 11 views 0 copies

Python code

46 lines
Python 3.9+
class 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

stdout
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

  1. Use `@singledispatchmethod` on the `visit` method for cleaner type-based dispatch.
  2. 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

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from OOP & classes

Related tutorials and quizzes for this topic.