Get AI summaries of any video or article — Sign up free
Python OOP Tutorial 5: Special (Magic/Dunder) Methods thumbnail

Python OOP Tutorial 5: Special (Magic/Dunder) Methods

Corey Schafer·
4 min read

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.

TL;DR

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?

__repr__ is intended for debugging and logging, aiming for an unambiguous representation. A common guideline is to return a string that could recreate the object in Python code. __str__ is intended for end users, so it returns a more readable summary. In the Employee example, __repr__ returns a formatted string including key fields (like names and pay) so printing the instance yields a precise representation, while __str__ returns something friendlier like the employee’s full name and email.

How does operator overloading work for the + operator in Python?

The + operator triggers the __add__ special method. Built-in types map + to their own behavior: integers add numerically, while strings concatenate. For a custom class, defining __add__(self, other) lets the class decide what “addition” means. The tutorial’s Employee __add__ returns self.pay + other.pay, so employee1 + employee2 produces combined salaries; without __add__, Python raises an error because it can’t add those objects.

What does len() call under the hood, and how can a class customize it?

len(x) calls the object’s __len__ method. After implementing __len__ on the Employee class, len(employee_instance) returns a computed value. The example returns the number of characters in self.full_name, so len(employee1) becomes the length of the employee’s name string.

What does returning NotImplemented accomplish in dunder arithmetic methods?

Returning NotImplemented signals that the operation isn’t supported for that operand type, without immediately throwing an error. Python then gives the other operand a chance to handle the operation via its corresponding method. If neither side can handle it, Python eventually raises an error. The tutorial points to datetime.timedelta.__add__ using this pattern when the other object isn’t a timedelta.

How can learning dunder methods make standard library code easier to read?

Many standard library behaviors—like how objects print or how arithmetic works—are implemented through dunder methods. By recognizing names such as __add__, __repr__, and __str__, it becomes easier to trace what happens when you use operators or built-in functions. The tutorial highlights datetime.date using ISO formatting for its string output by wiring __str__ to isoformat, and datetime.timedelta implementing __add__ with type checks.

Review Questions

  1. 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?
  2. In the Employee example, what does __add__ return, and what error occurs if __add__ is missing?
  3. How does len(employee) change after implementing __len__, and what value does the tutorial’s __len__ compute?

Key Points

  1. 1

    Special (dunder) methods use double underscores and control both object display and operator behavior in Python.

  2. 2

    Implementing __repr__ gives an unambiguous, developer-oriented representation, often shaped like code that could recreate the object.

  3. 3

    Implementing __str__ provides a readable, end-user display string; print(obj) uses __str__ when available.

  4. 4

    Operator overloading for + is driven by __add__, letting custom classes define what addition means (e.g., combined pay for Employee).

  5. 5

    Built-in functions map to dunder methods: len() calls __len__, so classes can define what “length” means.

  6. 6

    Returning NotImplemented from dunder arithmetic methods allows the other operand a chance to handle the operation before Python raises an error.

  7. 7

    Standard library types (like datetime.timedelta and datetime.date) rely on these same mechanisms for consistent arithmetic and formatting.

Highlights

__repr__ is designed for debugging and often follows a “copy-and-paste to recreate” rule, while __str__ targets end-user readability.
Defining __add__ on a class makes + work for that class—turning employee1 + employee2 into a combined-salary calculation.
Implementing __len__ lets len(obj) return a custom metric, such as the character count of an employee’s full name.
datetime.timedelta.__add__ uses type checks and returns NotImplemented when the other operand isn’t a timedelta, enabling fallback handling.
datetime.date wires its string output to ISO formatting, showing how dunder methods shape standard library display behavior.

Topics

  • Dunder Methods
  • Operator Overloading
  • __repr__ vs __str__
  • __add__ and __len__
  • NotImplemented

Mentioned