What is method chaining and how does it work in Python?
Method chaining is a pattern where multiple method calls are linked together in a single expression. Each method returns an object (usually self) that allows the next method to be called on it.
How it works:
- Each method performs its operation
- Each method returns
self(or a new object of the same type) - The caller can chain
.method()calls without intermediate variables
Two flavors:
- Mutable chaining: Methods modify
selfand returnself - Immutable chaining: Methods return a new object (like Django QuerySets)
Real-world examples:
- Django QuerySet:
User.objects.filter(...).exclude(...).order_by(...) - pandas:
df.dropna().groupby('col').mean() - SQLAlchemy:
session.query(User).filter(...).order_by(...).limit(10) - String methods:
' hello '.strip().upper().replace('H', 'J')
class Query:
def filter(self, **kwargs):
# apply filter
return self # enables chaining
def order_by(self, field):
# set ordering
return self # enables chaining
def limit(self, n):
# set limit
return self # enables chaining
# Usage:
result = Query().filter(active=True).order_by('name').limit(10)
# Basic method chaining with return self
class Query:
def __init__(self):
self._filters = []
self._order = None
self._limit = None
def filter(self, **kwargs):
self._filters.append(kwargs)
return self # return self for chaining
def order_by(self, field):
self._order = field
return self
def limit(self, n):
self._limit = n
return self
def build(self):
parts = ["SELECT * FROM table"]
for f in self._filters:
conditions = ' AND '.join(f"{k}={v!r}" for k, v in f.items())
parts.append(f"WHERE {conditions}")
if self._order:
parts.append(f"ORDER BY {self._order}")
if self._limit:
parts.append(f"LIMIT {self._limit}")
return ' '.join(parts)
# Chained usage
result = (Query()
.filter(active=True)
.filter(role='admin')
.order_by('name')
.limit(10)
.build())
print(result)
# SELECT * FROM table WHERE active=True WHERE role='admin' ORDER BY name LIMIT 10
# Python built-in chaining (strings are immutable -- each returns new string)
result = ' Hello, World! '.strip().lower().replace('world', 'python')
print(result) # 'hello, python!'
# List methods that DON'T support chaining (return None)
my_list = [3, 1, 2]
# WRONG: my_list.sort().append(4) -- sort() returns None!
# RIGHT:
my_list.sort()
my_list.append(4)
- •Each method returns self (or a new object) to enable chaining
- •Reduces intermediate variables and makes code more readable
- •Python strings naturally support chaining (each method returns new string)
- •Two styles: mutable (modify self) and immutable (return new object)
- •Many built-in list methods (sort, append, extend, reverse) return None -- NOT chainable
- •Mutable chaining modifies the original object -- callers may not expect this
- •Long chains can be hard to debug -- which call failed?
- •Returning self from __init__ is not possible (it must return None)