Aprende Python · lección gratuita

Lección 28 · Métodos especiales (dunder)

Resumen

Los métodos especiales (también llamados dunder, por double underscore, es decir doble guion bajo) son métodos con nombres como __str__ o __len__ que Python invoca automáticamente en respuesta a operaciones del lenguaje. Tú no los llamas con objeto.__str__(); en su lugar usas print(objeto), len(objeto) o a == b, y Python se encarga de invocar el dunder correcto por debajo. Definirlos en tus clases hace que tus objetos se integren con la sintaxis natural de Python.

Conceptos

Cuando escribes print(objeto), Python no imprime la dirección de memoria si tú le das una alternativa: busca un método __str__ en la clase del objeto y usa el texto que retorne. De forma parecida, len(objeto) busca __len__, y a == b busca __eq__. Estos "ganchos" permiten que tus clases se comporten como los tipos integrados (list, str, etc.). Por eso len([1, 2, 3]) funciona: las listas implementan __len__.

La diferencia entre __str__ y __repr__ es de propósito: __str__ está pensado para el usuario final (legible, bonito), mientras que __repr__ está pensado para el desarrollador (preciso, idealmente reconstruible). print() y str() prefieren __str__; la consola interactiva y repr() usan __repr__. Si solo defines uno, define __repr__, porque Python lo usa como respaldo de __str__.

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

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

    def __eq__(self, otro):
        return self.x == otro.x and self.y == otro.y

p = Punto(1, 2)
print(p)              # Python llama a __str__  ->  Punto(1, 2)
print(p == Punto(1, 2))  # Python llama a __eq__  ->  True

Ejemplos

# 1) __str__ controla lo que muestra print()
class Producto:
    def __init__(self, nombre, precio):
        self.nombre = nombre
        self.precio = precio
    def __str__(self):
        return f"{self.nombre}: ${self.precio}"

print(Producto("Café", 12))   # Café: $12

# 2) __len__ hace que len() funcione sobre tu objeto
class Carrito:
    def __init__(self):
        self.items = ["pan", "leche", "huevos"]
    def __len__(self):
        return len(self.items)

print(len(Carrito()))   # 3

# 3) __eq__ define el operador ==
class Color:
    def __init__(self, hex):
        self.hex = hex
    def __eq__(self, otro):
        return self.hex == otro.hex

print(Color("#fff") == Color("#fff"))   # True
print(Color("#fff") == Color("#000"))   # False

# 4) __repr__ se usa como respaldo de print() si no hay __str__
class Etiqueta:
    def __init__(self, texto):
        self.texto = texto
    def __repr__(self):
        return f"Etiqueta('{self.texto}')"

print(Etiqueta("oferta"))   # Etiqueta('oferta')
💡 Define siempre __repr__ en tus clases. Es el respaldo de __str__ y te ahorra horas de depuración al mostrar objetos claros en la consola y en los mensajes de error.

Cheatsheet

MétodoSe activa conDebe devolver
__str__(self)print(obj), str(obj)un str legible
__repr__(self)repr(obj), consola, respaldo de strun str técnico
__len__(self)len(obj)un int >= 0
__eq__(self, otro)obj == otroun bool
__init__(self, ...)Clase(...)nada (None)

---

← Herencia y super()Módulos y librería estándar →

Ver todas las lecciones de Aprende Python →