Métodos especiales en Python: __str__, __repr__, __len__, __eq__ y más

Los métodos especiales (también llamados "dunder methods" o "magic methods") son los que Python llama automáticamente cuando usas operadores, funciones built-in o ciertos patrones del lenguaje. Implementarlos en tus clases hace que se comporten como tipos nativos.

__str__ y __repr__

class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __repr__(self):
        return f"Vector({self.x!r}, {self.y!r})"

    def __str__(self):
        return f"({self.x}, {self.y})"

v = Vector(3, 4)
print(str(v))   # (3, 4)        — usa __str__
print(repr(v))  # Vector(3, 4)  — usa __repr__
print([v])      # [Vector(3, 4)]— colecciones usan __repr__

__len__ y __bool__

class Inventario:
    def __init__(self):
        self._items = []

    def agregar(self, item):
        self._items.append(item)

    def __len__(self):
        return len(self._items)

    def __bool__(self):
        return len(self._items) > 0  # False si está vacío

inv = Inventario()
print(len(inv))   # 0
if not inv:
    print("El inventario está vacío")

inv.agregar("laptop")
print(len(inv))   # 1
if inv:
    print(f"Inventario con {len(inv)} items")

__eq__ y __hash__

Si defines __eq__, Python pone __hash__ a None automáticamente (el objeto deja de ser hashable). Define ambos si necesitas que los objetos sean comparables Y usables en sets/dicts:

class Punto:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __eq__(self, otro):
        if not isinstance(otro, Punto):
            return NotImplemented
        return self.x == otro.x and self.y == otro.y

    def __hash__(self):
        return hash((self.x, self.y))

    def __repr__(self):
        return f"Punto({self.x}, {self.y})"

p1 = Punto(1, 2)
p2 = Punto(1, 2)
p3 = Punto(3, 4)

print(p1 == p2)   # True
print(p1 == p3)   # False

# Hashable: se puede usar en set y como clave de dict
puntos = {p1, p2, p3}
print(puntos)     # {Punto(1, 2), Punto(3, 4)} — solo 2 elementos

Operadores aritméticos

class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __add__(self, otro):
        return Vector(self.x + otro.x, self.y + otro.y)

    def __sub__(self, otro):
        return Vector(self.x - otro.x, self.y - otro.y)

    def __mul__(self, escalar):
        return Vector(self.x * escalar, self.y * escalar)

    def __rmul__(self, escalar):  # para 3 * v además de v * 3
        return self.__mul__(escalar)

    def __abs__(self):
        return (self.x**2 + self.y**2) ** 0.5

    def __repr__(self):
        return f"Vector({self.x}, {self.y})"

v1 = Vector(1, 2)
v2 = Vector(3, 4)
print(v1 + v2)    # Vector(4, 6)
print(v2 - v1)    # Vector(2, 2)
print(v1 * 3)     # Vector(3, 6)
print(3 * v1)     # Vector(3, 6)
print(abs(v2))    # 5.0

Context manager: __enter__ y __exit__

class Temporizador:
    import time

    def __enter__(self):
        self.inicio = __import__('time').perf_counter()
        return self

    def __exit__(self, tipo, valor, tb):
        self.duracion = __import__('time').perf_counter() - self.inicio
        print(f"Tiempo: {self.duracion:.4f}s")
        return False  # no suprime excepciones

with Temporizador() as t:
    resultado = sum(range(1_000_000))

print(t.duracion)  # acceso al atributo tras el bloque

La lista completa de dunder methods es larga, pero los más usados son __repr__, __str__, __len__, __eq__ y los aritméticos. Implementarlos bien hace que tus clases se integren de forma natural con el resto del lenguaje.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP