Python OOP Tutorial 5: Special (Magic/Dunder) Methods
Based on Corey Schafer's video on YouTube. If you like this content, support the original creators by watching, liking and subscribing to their content.
Special (dunder) methods use double underscores and control both object display and operator behavior in Python.
Briefing
Python’s “special” (magic/dunder) methods let custom classes behave like built-in types—changing how operations work and how objects display. Instead of seeing a vague “Employee object” when printing an instance, implementing methods like __repr__ and __str__ makes the output useful for developers and end users. These methods also power operator overloading: the same + symbol can mean integer addition, string concatenation, or—after customization—something entirely different for user-defined objects.
Core formatting behavior starts with __init__, which is implicitly called when an object is created to set attributes. For display, __repr__ and __str__ are the two most important. __repr__ is meant to be unambiguous and useful for debugging and logging; a common rule is to return a string that could recreate the object in Python code. In the tutorial’s Employee example, __repr__ returns a formatted string using the class name and key fields (like first name, last name, and pay), so printing the instance yields a copy-and-pasteable representation rather than a generic object label. __str__ is the readable counterpart for end users, returning a friendlier string such as the employee’s full name and email. When both exist, printing the object uses __str__, while developers can still call __repr__ explicitly.
Beyond printing, dunder methods drive arithmetic and built-in functions. The + operator maps to __add__. The tutorial demonstrates that integers use numeric addition, strings use concatenation, and a custom class can define __add__ to control what “adding” means. For Employee, __add__ takes self (left operand) and other (right operand) and returns combined pay, enabling employee1 + employee2 to produce a total salary. Without __add__, Python raises a TypeError because it doesn’t know how to add those objects.
The same pattern applies to other language features. len() relies on __len__. After implementing __len__ on Employee, len(employee_instance) returns a computed value—in the example, the character count of the employee’s full name. The tutorial notes that many more dunder methods exist for subtraction, multiplication, division, comparisons, and equality, and points viewers to Python’s documentation for the full list.
To show how this works in real libraries, the tutorial inspects standard library implementations. datetime.timedelta’s __add__ checks whether the other operand is also a timedelta; if not, it returns NotImplemented rather than immediately failing. That return value allows the other object’s method to try handling the operation first; if neither side can, Python eventually raises an error. The datetime.date class also implements __repr__ and sets __str__ behavior to output ISO-formatted dates, illustrating how standard library types use dunder methods to make objects display predictably.
Overall, the takeaway is practical: implementing a small set of dunder methods—especially __init__, __repr__, __str__, __add__, and __len__—turns plain classes into first-class citizens in Python’s syntax and tooling, improving both usability and debuggability.
Cornell Notes
Special (magic/dunder) methods let custom Python classes mimic built-in behavior. __repr__ provides an unambiguous, developer-friendly representation (often copy-and-pasteable), while __str__ provides a readable, end-user display. Operator overloading works through methods like __add__, so the + operator can be defined for objects such as Employee to return combined pay. Built-in functions like len() map to methods like __len__, letting objects define what “length” means. Standard library code (e.g., datetime.timedelta and datetime.date) uses these methods to implement consistent arithmetic and string formatting, including returning NotImplemented when types don’t match.
Why do __repr__ and __str__ both matter, and how do their goals differ?
How does operator overloading work for the + operator in Python?
What does len() call under the hood, and how can a class customize it?
What does returning NotImplemented accomplish in dunder arithmetic methods?
How can learning dunder methods make standard library code easier to read?
Review Questions
- Which method should you implement if you want print(employee) to show a user-friendly string, and which method should you implement for debugging-friendly output?
- In the Employee example, what does __add__ return, and what error occurs if __add__ is missing?
- How does len(employee) change after implementing __len__, and what value does the tutorial’s __len__ compute?
Key Points
- 1
Special (dunder) methods use double underscores and control both object display and operator behavior in Python.
- 2
Implementing __repr__ gives an unambiguous, developer-oriented representation, often shaped like code that could recreate the object.
- 3
Implementing __str__ provides a readable, end-user display string; print(obj) uses __str__ when available.
- 4
Operator overloading for + is driven by __add__, letting custom classes define what addition means (e.g., combined pay for Employee).
- 5
Built-in functions map to dunder methods: len() calls __len__, so classes can define what “length” means.
- 6
Returning NotImplemented from dunder arithmetic methods allows the other operand a chance to handle the operation before Python raises an error.
- 7
Standard library types (like datetime.timedelta and datetime.date) rely on these same mechanisms for consistent arithmetic and formatting.