How to Understand return self
in Python Class Methods
The return self
statement in a Python class method serves two primary purposes: enabling method chaining and implementing the iterator protocol.
This guide explains both use cases, providing clear examples and guidelines for when (and when not) to use return self
.
Method Chaining with return self
Method chaining allows you to call multiple methods on the same object in a single, continuous expression. This can make code more readable and concise, especially when performing a sequence of related operations on an object. return self
is the key to enabling this pattern. Without return self
(or returning some other object that you want to continue working with), the chain breaks.
class Calc():
def __init__(self, number=0):
self.number = number
def add(self, value):
self.number = self.number + value
return self # Return the object itself
def subtract(self, value):
self.number = self.number - value
return self # Return the object itself
def display(self): # A method that DOESN'T return self
print(self.number)
calc = Calc()
calc.add(5).subtract(2).add(5) # Method chaining
print(calc.number) # Output: 8
calc.subtract(5).add(3) # Chaining works in any order
print(calc.number) # Output: 6
# Example showing the difference with a method that DOESN'T return self:
calc.add(5).display() # Output: 11
# calc.add(5).display().subtract(2) # ⛔️ AttributeError: 'NoneType' object has no attribute 'subtract'
return self
: Each method (add
andsubtract
) modifies the object's state (self.number
) and then returns the object itself (self
).- Chaining: Because each method returns the object, you can immediately call another method on the result.
calc.add(5)
returns thecalc
object, so you can then call.subtract(2)
on it, and so on. This creates a "fluent interface." display()
Doesn't Returnself
: Thedisplay()
method prints the value but doesn't return anything (implicitly returnsNone
). You can't chain further method calls afterdisplay()
. This illustrates the importance ofreturn self
for chaining.
A More Illustrative Example
Consider a class representing a string builder:
class StringBuilder:
def __init__(self):
self.string = ""
def append(self, text):
self.string += text
return self
def prepend(self, text):
self.string = text + self.string
return self
def to_string(self): # Doesn't return self
return self.string
builder = StringBuilder()
result = builder.append("Hello").append(", ").prepend("Greetings: ").to_string()
print(result) # Output: Greetings: Hello,
This example is more practical. StringBuilder
lets you build up a string piece by piece. The append
and prepend
methods return self
, allowing chaining. The to_string()
method returns the final string (and therefore doesn't return self
).
Implementing the Iterator Protocol with return self
The second major use of return self
is in the __iter__()
method of a class to make it iterable.
class Counter:
def __init__(self, start, stop):
self.current = start - 1 # Start one *before* the desired starting value
self.stop = stop
def __iter__(self):
return self # Return the object itself (THIS IS THE KEY)
def __next__(self):
self.current += 1 # Increment the counter
if self.current < self.stop:
return self.current # Return the next value
raise StopIteration # Signal the end of iteration
for c in Counter(0, 4): # You can use the class directly in a loop
print(c)
Output:
0
1
2
3
__iter__(self)
: This method is called when an iterator is requested for the object (e.g., at the start of afor
loop). It must return an iterator object. In many cases (including this one), the object is its own iterator, so wereturn self
.__next__(self)
: This method is called to get the next item in the sequence. Itraise StopIteration
when there are no more items. This is how the loop knows when to stop.- Key Point: In an iterator, the
__iter__()
method almost alwaysreturn self
.
When NOT to Use return self
- Methods that return a specific value: If a method's purpose is to compute and return a specific result (e.g., a calculation, a boolean, a new object), don't
return self
. Return the actual result. - Methods with no logical return value: If a method's primary purpose is to modify the object's state, and it doesn't have a meaningful value to return, then a simple
return
(without any value), or even noreturn
statement at all, is perfectly fine. Python implicitly returnsNone
in this case. Don'treturn self
just for the sake of it.
Example
def do_math(a,b):
return a+b # Returns the sum of the arguments.
result = do_math(5, 5)
print(result) # Output: 10
def print_value(val):
print(val)
return # No logical return value, so we return None.
Conclusion
The return self
statement in Python class methods has two important, distinct uses:
- Method Chaining:
return self
allows you to create a fluent interface, making code more readable and concise when performing a sequence of operations on an object. - Iterator Protocol: In the
__iter__()
method of a class,return self
is the standard way to indicate that the object itself is its own iterator.
Use return self
strategically and appropriately, and avoid it when a method has a different logical return value or no return value at all. This will make your code clearer and easier to maintain.