Dunder Methods
Operator overloading: TypeScript can’t, Python can
Section titled “Operator overloading: TypeScript can’t, Python can”TypeScript has no operator overloading. If you build a Vector class, you must call .add(other) explicitly — writing v1 + v2 is never valid for custom types. Python solves this through dunder methods (short for “double underscore”), also called magic methods. They are ordinary methods with names like __add__, __str__, and __len__ that Python calls automatically when you use operators or built-in functions on your objects.
The name “dunder” is Python community shorthand for names that begin and end with two underscores. You define them yourself; Python invokes them behind the scenes.
Defining vector addition
Section titled “Defining vector addition”The most illustrative example is a 2D vector. In TypeScript you must name the operation explicitly. In Python you hook into + by defining __add__.
// TypeScript: no operator overloading — you must name the methodclass Vector { constructor(public x: number, public y: number) {}
toString(): string { return `Vector(${this.x}, ${this.y})`; }
add(other: Vector): Vector { return new Vector(this.x + other.x, this.y + other.y); }
equals(other: Vector): boolean { return this.x === other.x && this.y === other.y; }
get length(): number { return 2; }}
const v1 = new Vector(1, 2);const v2 = new Vector(3, 4);const v3 = v1.add(v2); // must call .add(), can't write v1 + v2console.log(v3.toString()); // "Vector(4, 6)"# Python: operator overloading via dunder methodsclass Vector: def __init__(self, x, y): self.x = x self.y = y
def __str__(self): return f"Vector({self.x}, {self.y})"
def __repr__(self): return f"Vector({self.x!r}, {self.y!r})"
def __len__(self): return 2
def __eq__(self, other): return self.x == other.x and self.y == other.y
def __add__(self, other): return Vector(self.x + other.x, self.y + other.y)
v1 = Vector(1, 2)v2 = Vector(3, 4)v3 = v1 + v2 # calls v1.__add__(v2) automaticallyprint(str(v1)) # calls v1.__str__()print(len(v1)) # calls v1.__len__()print(v1 == Vector(1, 2)) # calls v1.__eq__(...)How Python dispatches dunder calls
Section titled “How Python dispatches dunder calls”When Python evaluates v1 + v2, it internally calls type(v1).__add__(v1, v2). When you call str(v1), Python calls v1.__str__(). When you call len(v1), Python calls v1.__len__(). This means every built-in function and operator has a corresponding dunder hook you can implement.
There is also __repr__, which differs from __str__: str() is meant for end-user display while repr() (and the interactive REPL) use __repr__ for unambiguous, developer-facing output. The !r conversion flag in f-strings calls repr() on the value.
Try it
Section titled “Try it”class Vector: def __init__(self, x, y): self.x = x self.y = y
def __str__(self): return f"Vector({self.x}, {self.y})"
def __repr__(self): return f"Vector({self.x!r}, {self.y!r})"
def __len__(self): return 2
def __eq__(self, other): return self.x == other.x and self.y == other.y
def __add__(self, other): return Vector(self.x + other.x, self.y + other.y)
v1 = Vector(1, 2)v2 = Vector(3, 4)v3 = v1 + v2
print(str(v1))print(len(v1))print(v1 == Vector(1, 2))print(v3)Loading Python runtime (first run only)…