How to Build a Fluent Interface with the Builder Pattern in Python

Learn to implement a fluent builder pattern in Python by chaining methods that return self, enabling readable object construction.

Easy Python 3.9+ Aug 9, 2026 OOP & classes 15 views 0 copies

Python code

33 lines
Python 3.9+
class Pizza:
    def __init__(self):
        self.size = None
        self.toppings = []
        self.crust = None

    def set_size(self, size):
        self.size = size
        return self

    def add_topping(self, topping):
        self.toppings.append(topping)
        return self

    def set_crust(self, crust):
        self.crust = crust
        return self

    def build(self):
        return self

    def __str__(self):
        return f"Pizza(size={self.size}, crust={self.crust}, toppings={self.toppings})"


if __name__ == "__main__":
    pizza = (Pizza()
             .set_size("large")
             .add_topping("mushrooms")
             .add_topping("olives")
             .set_crust("thin")
             .build())
    print(pizza)

Output

stdout
Pizza(size=large, crust=thin, toppings=['mushrooms', 'olives'])

How it works

The Builder pattern separates object construction from its representation. Each setter method returns self, enabling method chaining and a fluent interface. This makes the code more readable and reduces the chance of passing wrong arguments to constructors. The build method returns the finalized object, which here is the same instance, but it can be extended to validate or create immutable copies. By returning self from each method, you create a consistent, chainable API that improves code flow and readability.

Common mistakes

  • Forgetting to return `self` from setter methods, breaking the chain.
  • Assuming `build` creates a new object; it returns the same instance, so mutations after build affect the original.
  • Not initializing mutable attributes (like `toppings`) with a fresh list in `__init__`, causing shared state across instances.

Variations

  1. Use dataclasses and a separate builder class to enforce required fields and immutability.
  2. Implement a `copy()` method to return a clone of the builder at each step, preventing side effects.

Real-world use cases

  • Building complex HTTP request objects with many optional headers and parameters in a readable chain.
  • Configuring database connection settings step-by-step in a setup script with clear optional flags.
  • Constructing nested report objects in a data pipeline, where each field is optional and defaults matter.

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.