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
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 byprint())__repr__: Developer/debug string (used by REPL,repr())__eq__: Equality comparison (==)__lt__,__gt__,__le__,__ge__: Ordering comparisons__len__: Support forlen()__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