Dev.to AI πŸ€– Ai πŸ‘ 0 πŸ“– 2 min read

Mastering Python Dunder Methods: Building Production-Grade Value Objects

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 syst

Mastering Python Dunder Methods: Building Production-Grade Value Objects

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.

  1. 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.

πŸ“° Read the original article on Dev.to AI

Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes β€” full credit and traffic to the original publisher.