Python Operator Overloading: Make Classes Work with +, -, ==
The + operator means different things in different contexts. 3 + 5 gives you 8. 'hello' + ' world' gives you 'hello world'. [1, 2] + [3, 4] gives you [1, 2, 3, 4]. Same symbol, completely different behavior.
How does Python know what + should do? It asks the object on the left side by calling its __add__ method. Integers define __add__ to do math. Strings define it to concatenate. Lists define it to merge.
In this tutorial, you'll learn how to define these methods in your own classes. By the end, your custom objects will work with +, -, *, ==, <, +=, and more — just like built-in types.
How Do Arithmetic Operators Work with Custom Classes?
Each arithmetic operator maps to a specific magic method. When you write a + b, Python calls a.__add__(b). When you write a - b, Python calls a.__sub__(b). Here's the full mapping:
Let's build a Money class where + combines amounts and - finds the difference.
What Are Reflected Operators Like __radd__?
What happens when you write 3 * money? Python first tries (3).__mul__(money). But integers don't know about your Money class, so __mul__ returns NotImplemented. Python then tries the reflected version: money.__rmul__(3).
Reflected operators have an r prefix: __radd__, __rsub__, __rmul__, etc. They handle the case where your object is on the right side of the operator.
How Do Comparison Operators Work?
Comparison operators follow the same pattern as arithmetic ones. Each operator calls a specific magic method:
What Are In-Place Operators Like __iadd__?
In-place operators like +=, -=, and *= are different from regular ones. a += b is not always the same as a = a + b. With __iadd__, you can modify the object in place instead of creating a new one.
This matters for performance when modifying large objects. Lists use this optimization: my_list += [4, 5] modifies the list in place, while my_list = my_list + [4, 5] creates an entirely new list.
If you don't define __iadd__, Python falls back to __add__. So a += b becomes a = a.__add__(b) — creating a new object. This is fine for immutable types, but wasteful for mutable ones.
How Do You Build a Complete Vector Class?
Let's put everything together by building a 2D Vector class that supports addition, subtraction, multiplication (by a scalar), equality, comparisons (by length), and in-place operations.
Notice how the Vector class uses @property for length (a computed attribute from the previous tutorial) and how __lt__ compares vectors by their length. This is a natural way to define "less than" for vectors.
What Are the Most Common Operator Overloading Patterns?
Here is a quick reference of the most important operator methods you'll use. You don't need to memorize all of them — just know they exist so you can look them up when needed.
Practice Exercises
Create a Point class with x and y attributes. Add __add__ so that adding two points creates a new point with summed coordinates. Add __repr__ that returns 'Point(<x>, <y>)'. Create Point(1, 2) and Point(3, 4), add them together, and print the result.
Create a Vector class with x and y. Add __mul__ so vector * number scales both components. Add __rmul__ so number * vector also works. Add __repr__ returning 'Vector(<x>, <y>)'. Print Vector(3, 4) * 2 and 3 * Vector(1, 5).
This Score class uses __iadd__ but the variable becomes None after +=. Find and fix the bug.
Read the code carefully and predict exactly what gets printed. Think about which magic methods are called for each operation.
Create a Fraction class with num (numerator) and den (denominator). Add __add__ that adds two fractions using the formula (a/b + c/d = (a*d + c*b) / (b*d)). Add __eq__ that checks if two fractions are equal by cross-multiplying (a/b == c/d when a*d == b*c). Add __repr__ returning '<num>/<den>'. Create Fraction(1, 2) and Fraction(1, 3), add them, and print the result. Then print whether Fraction(1, 2) == Fraction(2, 4).
Refactor this Wallet class to use operator overloading. Replace add_money(amount) with __iadd__, subtract_money(amount) with __isub__, and equals(other) with __eq__. Add __repr__ returning 'Wallet(<balance>)'. Then create Wallet(100), add 50, subtract 30, print the wallet, and print whether it equals Wallet(120).