Skip to content

📙 Clase 17 — Métodos dunder (__str__, __eq__…)

Fase 3 · Programación Orientada a Objetos ⬅️ Volver al índice de clases

🎯 Qué aprendí

  • Los métodos dunder (double underscore) conectan tu clase con la sintaxis de Python.
  • __str__/__repr__ (imprimir), __len__ (medir), __eq__ (comparar).
  • Ya conocía uno: __init__. Son la razón de que len(), == y print() "sepan" qué hacer.

📖 PARTE TEÓRICA

🪄 1. ¿Qué son?

Los dunder (__asi__) son métodos con nombre especial que Python llama por ti cuando usas su sintaxis:

Tú escribesPython llamaPara
Contacto("Ana","1")__init__construir (¡ya lo usas!)
print(obj) / str(obj)__str__texto "bonito" para humanos
obj en consola / repr(obj)__repr__texto técnico para programadores
len(obj)__len__medir
a == b__eq__comparar por contenido
a < b__lt__ordenar (sorted lo usa)
for x in obj__iter__recorrer
obj[i]__getitem__indexar

🖨️ 2. __str__ y __repr__: imprimir con sentido

Sin ellos, imprimir un objeto muestra basura técnica:

python
class SinStr:
    pass
print(SinStr())        # <__main__.SinStr object at 0x104f81a30>   😕

Con ellos:

python
class Playlist:
    def __init__(self, nombre, canciones):
        self.nombre = nombre
        self.canciones = canciones

    def __str__(self):                 # para el USUARIO (print, f-strings)
        return f"Playlist '{self.nombre}' con {len(self.canciones)} canciones"

    def __repr__(self):                # para el PROGRAMADOR (debug, consola)
        return f"Playlist({self.nombre!r}, {self.canciones!r})"

pl = Playlist("Focus", ["a", "b"])
print(pl)          # Playlist 'Focus' con 2 canciones
print(repr(pl))    # Playlist('Focus', ['a', 'b'])

🧪 Tip de entrevista: "¿__str__ vs __repr__?" → __str__ es legible para el usuario final; __repr__ es inequívoco para debug (idealmente, código que recrea el objeto). Si solo defines __repr__, print lo usa de respaldo.

📏 3. __len__ y __eq__

python
class Playlist:
    # …(lo de arriba)…
    def __len__(self):
        return len(self.canciones)

    def __eq__(self, otra):
        return self.canciones == otra.canciones

pl = Playlist("Focus", ["a", "b"])
print(len(pl))                              # 2       ← len() ya funciona
print(pl == Playlist("Otra", ["a", "b"]))   # True    ← compara CONTENIDO

⚠️ Sin __eq__, a == b compara identidad (¿son el mismo objeto en memoria?), no contenido. Por eso dos Contacto("Ana","1") idénticos dan False hasta que defines __eq__.


🖥️ EN TU APP DE ESCRITORIO

Los dunder hacen que tus modelos se integren natural con la GUI y con el debug:

python
class Tarea:
    def __init__(self, texto):
        self.texto = texto
        self.hecha = False

    def __str__(self):                       # lo que muestra la GUI
        return f"{'✅' if self.hecha else '⬜'} {self.texto}"

    def __eq__(self, otra):                  # para detectar duplicados
        return self.texto.lower() == otra.texto.lower()

# En la GUI, pintar es directo — el f-string llama a __str__ solo:
for tarea in tareas:
    ctk.CTkLabel(lista, text=f"{tarea}").pack()

# Y evitar duplicados se lee como español:
nueva = Tarea(entry.get())
if nueva in tareas:                          # 'in' usa __eq__ por detrás
    label_error.configure(text="Esa tarea ya existe")

💡 nueva in tareas funcionando "gratis" gracias a __eq__ — el operador in de la Clase 01 compara con == elemento a elemento.


🗄️ CON BASE DE DATOS (caso de uso)

__repr__ vale oro al depurar lo que sale de la BD:

python
filas = [Contacto(n, t) for n, t in con.execute("SELECT nombre, tel FROM contactos")]
print(filas)
# Sin __repr__: [<Contacto object at 0x…>, <Contacto object at 0x…>]   😖
# Con __repr__: [Contacto('Ana', '999-111'), Contacto('Luis', '988-222')]   😍

🏋️ EJERCICIOS CON SOLUCIÓN

Ejercicio 1 — Imprimible

Dale a Producto(nombre, precio) un __str__"Teclado — S/120".

Ver solución
python
class Producto:
    def __init__(self, nombre, precio):
        self.nombre = nombre
        self.precio = precio

    def __str__(self):
        return f"{self.nombre} — S/{self.precio}"

print(Producto("Teclado", 120))   # Teclado — S/120

Ejercicio 2 — Comparable

Haz que dos Producto sean iguales si comparten nombre (ignorando mayúsculas).

Ver solución
python
class Producto:
    def __init__(self, nombre, precio):
        self.nombre = nombre
        self.precio = precio

    def __eq__(self, otro):
        return self.nombre.lower() == otro.nombre.lower()

print(Producto("Mouse", 45) == Producto("MOUSE", 60))   # True (mismo nombre)

Ejercicio 3 — Medible

Crea Curso(nombre, alumnos) (lista) con __len__, y comprueba len(curso).

Ver solución
python
class Curso:
    def __init__(self, nombre, alumnos):
        self.nombre = nombre
        self.alumnos = alumnos

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

c = Curso("Python", ["Ana", "Luis", "Marta"])
print(len(c))   # 3

❓ Preguntas y respuestas (autoevaluación)

1. ¿Quién llama a los métodos dunder?

Python, automáticamente, cuando usas su sintaxis (print, ==, len, in, []…).

2. ¿Qué compara == entre dos objetos sin __eq__?

Identidad (si son el mismo objeto en memoria), no contenido.

3. ¿Cuál es para el usuario y cuál para el programador: __str__/__repr__?

__str__ → usuario (print); __repr__ → programador (debug/consola). print usa __repr__ como respaldo si no hay __str__.

4. ¿Qué dunder hace funcionar if nueva in tareas?

__eq__in compara con == contra cada elemento.


📎 Apuntes relacionados

➡️ Siguiente

Clase 18 · Tu app como clase — el gran refactor: juntar TODA la Fase 3.