The OP set themselves up for a fall by casting the Builder pattern as Java specific. It's not. It's in the Gang of Four's book and is potentially relevant to any object oriented language.
Unfortunately, even the Wikipedia article on the Builder pattern doesn't give it enough credit. It's not simply useful for code elegance. Builder patterns are a great way to create immutable objects that need to be mutable until they're used. Immutable state is especially critical in functional paradigms, making the Builder an excellent object-oriented pattern for python.
I've provided an an example Builder + ImmutableObject implementation below using the collections.namedtuple, borrowed and modified from "How to make an immutable object in python". I've kept the Builder fairly simple. However, setter functions could be provided that return the Builder itself to allow call chaining. Or @property syntax could be used in the Builder to provide attribute setters that check attribute validity prior to setting.
from collections import namedtuple
IMMUTABLE_OBJECT_FIELDS = ['required_function_result', 'required_parameter', 'default_parameter']
class ImmutableObjectBuilder(object):
def __init__(self, required_function, required_parameter, default_parameter="foo"):
self.required_function = required_function
self.required_parameter = required_parameter
self.default_parameter = default_parameter
def build(self):
return ImmutableObject(self.required_function(self.required_parameter),
self.required_parameter,
self.default_parameter)
class ImmutableObject(namedtuple('ImmutableObject', IMMUTABLE_OBJECT_FIELDS)):
__slots__ = ()
@property
def foo_property(self):
return self.required_function_result + self.required_parameter
def foo_function(self):
return self.required_function_result - self.required_parameter
def __str__(self):
return str(self.__dict__)
Example usage:
my_builder = ImmutableObjectBuilder(lambda x: x+1, 2)
obj1 = my_builder.build()
my_builder.default_parameter = "bar"
my_builder.required_parameter = 1
obj2 = my_builder.build()
my_builder.required_function = lambda x: x-1
obj3 = my_builder.build()
print obj1
# prints "OrderedDict([('required_function_result', 3), ('required_parameter', 2), ('default_parameter', 'foo')])"
print obj1.required_function_result
# prints 3
print obj1.foo_property
# prints 5
print obj1.foo_function()
# prints 1
print obj2
# prints "OrderedDict([('required_function_result', 2), ('required_parameter', 1), ('default_parameter', 'bar')])"
print obj3
# prints "OrderedDict([('required_function_result', 0), ('required_parameter', 1), ('default_parameter', 'bar')])"
In this example, I created three ImmutableObjects, all with different parameters. I've given the caller the ability to copy, modify, and pass around a mutable configuration in the form of the builder while still guaranteeing immutability of the built objects. Setting and deleting attributes on the ImmutableObjects will raise errors.
Bottom line: Builders are a great way to pass around something with mutable state that provides an object with immutable state when you're ready to use it. Or, stated differently, Builders are a great way to provide attribute setters while still ensuring immutable state. This is especially valuable in functional paradigms.