ReviseAlgo Logo

Object-Oriented Programming

Dunder Methods

Magic methods for operator overloading and Python internals

Interview: Advanced Python — tests understanding of Python data model, operator overloading, and protocol methods

Last Updated: June 12, 2026 8 min read

Dunder (double underscore) methods, also called "magic methods," are special methods that Python calls implicitly. They define how objects behave with built-in operations like +, ==, len(), print(), iteration, and more. Mastering dunder methods is key to creating Pythonic classes.

Essential Dunder Methods

  • __init__: Constructor (object initialization)
  • __str__: Human-readable string (used by print())
  • __repr__: Developer/debug string (used by REPL, repr())
  • __eq__: Equality comparison (==)
  • __lt__, __gt__, __le__, __ge__: Ordering comparisons
  • __len__: Support for len()
  • __getitem__, __setitem__: Indexing and slicing
  • __iter__, __next__: Iteration protocol
  • __enter__, __exit__: Context manager protocol

__str__ vs __repr__

__str__ should return a user-friendly string, while __repr__ should return an unambiguous string that could recreate the object. If you define only one, define __repr__ — Python falls back to it for __str__.

Operator Overloading

Dunder methods let you define how operators work with your objects. This makes custom classes feel like built-in types.

Common Pitfall

When you override __eq__, Python sets __hash__ to None (object becomes unhashable). If you need the object in sets or as dict keys, you must also implement __hash__.

Use Cases

Operator overloading for mathematical objects (vectors, matrices, polynomials)

Custom string representations for debugging and display

Making objects iterable for use in for loops and comprehensions

Context managers for automatic resource management (files, connections)

Callable objects for function-like behavior with state

Common Mistakes

Defining __eq__ without __hash__ — makes objects unhashable (can't use in sets/dicts)

Not implementing __repr__ — debugging becomes difficult with default <__main__.Foo object>

Returning wrong types from operators (e.g., __add__ should return new object, not modify self)

Forgetting that __str__ falls back to __repr__ if not defined

Implementing __exit__ incorrectly — return True suppresses exceptions, False propagates them