Fluent Interface is an implementation of API which improves readability.

Example

Poem('The Road Not Taken').indent(4).suffix('Robert Frost') .

Fluent Interface is similar to method chaining. I was wondering how to implement this in Python .Returning self during method call seemed good idea .

class Poem(object): def __init__(self, content): self.content = content def indent(self, spaces=4): self.content = " " * spaces + self.content return self def suffix(self, content): self.content += " - {}".format(content) return self def __str__(self): return self.content >>>print Poem('Road Not Taken').indent(4).suffix('Rober Frost').content Road Not Taken - Rober Frost

Everything seems to be ok here.

Side effects

Above approach has side effect. We are mutating the same object during every method call.Consider the following code

>>>p = Poem('Road Not Taken') >>>q = p.indent(4) >>>r = p.indent(2) >>>print str(q) == str(r) True >>>print id(q), id(r) 4459640464 4459640464

Clearly this isn’t expected. q and r is pointing to same instance.Ideally we should create a new instance during every method call and return the object.

New object

Decorator is a function which takes a function as argument and returns a function (most cases).

from functools import wraps def newobj(method): @wraps(method) # Well, newobj can be decorated with function, but we will cover the case # where it decorated with method def inner(self, *args, **kwargs): obj = self.__class__.__new__(self.__class__) obj.__dict__ = self.__dict__.copy() method(obj, *args, **kwargs) return obj return inner class NPoem(object): def __init__(self, content): self.content = content @newobj def indent(self, spaces=4): self.content = " " * spaces + self.content @newobj def suffix(self, content): self.content += " - {}".format(content) def __str__(self): return self.content >>>p = NPoem("foo") >>>q = p.indent(4) >>>r = p.indent(2) >>>print str(q) == str(r) False >>>print(q) foo >>>print(r) foo >>>print id(q), id(r) 4459642640 4459639248

In the above approach newobj decorator creates a new instance, copies all the atrributes,calls the method with arguments and returns new instance . Rather than using decoratorprivate function can do the same.

Fluent Interface is used heavily in SQLAlchemy and Django. Djangouses _clone method to create new QuerySet andSQLAlchemy usesdecorator based approach to create new Query instance.

Thanks Mike Bayer and Daniel Roy Greenfeldhelping me understand this.

Do you know any other way to implement this ? Feel free to comment.

See also