M_ost Python developers know how to write a basic class. They define an __init_ method, maybe a few custom functions, and call it a day.
But in production-grade backend engineering, especially when building complex systems with FastAPI and AI integrations, basic classes aren't enough. You need Value Objects—objects whose equality, ordering, and hashing are determined entirely by their data, not by where they live in memory.
To do this, you have to master Dunder Methods (Double Underscore methods).
Here is how I build robust, Pythonic classes using the data model.
- Representation: repr vs str
You need two ways for your object to represent itself as a string.
· repr is for developers. It should ideally be an unambiguous string that could recreate the object.
· str is for end-users. It should be readable and clean.
class Money:
def __init__(self, amount: float, currency: str):
self.amount = amount
self.currency = currency
def __repr__(self) -> str:
return f"Money(amount={self.amount}, currency='{self.currency}')"
def __str__(self) -> str:
return f"{self.amount} {self.currency}"
2. Equality and Hashing: eq and hash
If two objects hold the same data (e.g., $10 USD), they should be considered equal. If you implement eq, you must also implement hash so the object can be used in sets or as dictionary keys.
def __eq__(self, other: object) -> bool:
if not isinstance(other, Money):
return NotImplemented
return self.amount == other.amount and self.currency == other.currency
def __hash__(self) -> int:
return hash((self.amount, self.currency))
Pro Tip: Returning NotImplemented instead of False when comparing to a non-Money object allows Python's comparison machinery to try the other object's eq method.
3. Ordering: lt and @total_ordering
To sort objects, Python uses lt (less than). But you don't need to write gt, le, and ge manually. Just use the functools module.
from functools import total_ordering
@total_ordering
class Money:
# ... init and eq ...
def __lt__(self, other: object) -> bool:
if not isinstance(other, Money):
return NotImplemented
if self.currency != other.currency:
raise TypeError("Cannot order amounts in different currencies.")
return self.amount < other.amount
The @total_ordering decorator looks at your eq and lt methods and automatically generates >, <=, and >= for you.
**
**
When you build AI pipelines or API backends, data consistency is everything. By implementing dunder methods correctly, you ensure that your custom objects can be sorted, deduplicated, and used as dictionary keys without silent bugs creeping into your database.
Mastering the Python Data Model is the difference between writing scripts and engineering systems.
Top comments (0)