Data Model Customization — Core Concepts
What Is the Data Model?
Python’s data model is the framework that defines how objects interact with the language itself. When you write a + b, Python doesn’t hard-code addition — it calls a.__add__(b). When you write len(x), Python calls x.__len__(). When you write for item in collection, Python calls collection.__iter__().
This design means any object can participate in Python’s syntax — as long as it implements the right special methods.
The Major Categories
Representation and Display
| Method | Triggered By | Purpose |
|---|---|---|
__repr__ | repr(obj), debugger | Unambiguous developer representation |
__str__ | str(obj), print(obj) | Human-readable display |
__format__ | f"{obj:spec}", format(obj) | Custom formatting |
__bool__ | if obj:, bool(obj) | Truthiness |
Rule of thumb: __repr__ should be unambiguous (ideally copy-pasteable to recreate the object). __str__ should be readable.
Comparison
| Method | Operator |
|---|---|
__eq__ | == |
__ne__ | != |
__lt__ | < |
__le__ | <= |
__gt__ | > |
__ge__ | >= |
Implementing __eq__ and __lt__ (plus using @functools.total_ordering) gives you all six comparisons.
Arithmetic
| Method | Operator | Reflected Version |
|---|---|---|
__add__ | + | __radd__ |
__sub__ | - | __rsub__ |
__mul__ | * | __rmul__ |
__truediv__ | / | __rtruediv__ |
__mod__ | % | __rmod__ |
__pow__ | ** | __rpow__ |
Reflected methods (__radd__, etc.) are called when the left operand doesn’t know how to handle the operation. If a + b fails via a.__add__(b), Python tries b.__radd__(a).
Container Behavior
| Method | Makes Object Work Like |
|---|---|
__len__ | len(obj) |
__getitem__ | obj[key], iteration fallback |
__setitem__ | obj[key] = value |
__delitem__ | del obj[key] |
__contains__ | item in obj |
__iter__ | for item in obj |
__reversed__ | reversed(obj) |
Callable Objects
| Method | Triggered By |
|---|---|
__call__ | obj() |
Making an object callable is powerful for creating function-like objects that maintain state.
Context Managers
| Method | Triggered By |
|---|---|
__enter__ | with obj as x: (start) |
__exit__ | End of with block |
How Operators Actually Work
When Python encounters a + b:
- Check if
type(b)is a subclass oftype(a). If so, tryb.__radd__(a)first (gives subclasses priority). - Try
a.__add__(b). - If that returns
NotImplemented, tryb.__radd__(a). - If that also returns
NotImplemented, raiseTypeError.
The NotImplemented singleton (not NotImplementedError!) is Python’s way for an operator method to say “I don’t know how to handle this type.”
Building a Complete Custom Type
A well-integrated custom type typically implements:
__init__— construction__repr__and__str__— representation__eq__and__hash__— equality and hashing (needed for sets and dict keys)- Domain-specific operators — whatever makes sense for the type
Why __hash__ Matters
If you implement __eq__, Python automatically sets __hash__ to None, making your object unhashable. If you need the object to work as a dictionary key or set member, you must also implement __hash__ — and ensure equal objects have equal hashes.
Common Misconception
“Special methods are just syntactic sugar.” They’re more than that. Many special methods are called by CPython’s C-level infrastructure directly, bypassing normal attribute lookup for performance. For example, len() calls the sq_length slot at the C level, not __len__ through Python’s attribute system. This means you can’t intercept these calls with __getattribute__.
Practical Tips
- Start with
__repr__— it’s the most useful single special method. Goodreprmakes debugging 10x easier. - Return
NotImplementedfrom operator methods when you receive an unfamiliar type — don’t raiseTypeErroryourself. - Don’t implement operators that don’t make sense. A
Userclass shouldn’t support+just because it can. - Use
@functools.total_orderingto avoid writing all six comparison methods manually.
One thing to remember: Python’s data model is a set of special methods that let your objects plug into the language’s syntax. Implement the right methods, and your objects can use +, in, for, with, len(), and more — becoming first-class citizens of the language.
See Also
- Python Attribute Lookup Chain How Python finds your variables and methods — like checking your pockets, then your bag, then your locker, in a specific order every time.
- Python Bytecode And Interpreter How your .py file turns into tiny instructions the Python interpreter can execute step by step.
- Python Class Body Execution Python runs the code inside your class definition immediately — like reading a recipe out loud before anyone starts cooking.
- Python Garbage Collection See how Python cleans up unreachable objects, especially the tricky ones that point at each other.
- Python Gil Why Python threads can feel stuck in traffic, and how the GIL explains the behavior.