Lecciones del módulo (3/4)
Métodos especiales (dunder)
Los métodos especiales (llamados dunder methods porque comienzan y terminan
con un doble guion bajo: __name__) son los ganchos (hooks) que permiten a tus objetos
integrarse con el lenguaje: representación de texto, comparación con ==,
soporte para len(), in, operadores, etc.
Ya has utilizado uno: __init__.
__str__ y __repr__
Controlan lo que aparece al usar print() y en el REPL.
class Punto:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Punto({self.x}, {self.y})"
def __str__(self):
return f"({self.x}, {self.y})"
p = Punto(3, 4)
str(p) # '(3, 4)' — para humanos
repr(p) # 'Punto(3, 4)' — para depurador / dev
print(p) # '(3, 4)'
p # Punto(3, 4) en el REPL → repr
[p, p] # [Punto(3, 4), Punto(3, 4)] — siempre repr en los contenedoresSi defines solo __repr__, Python también lo usa como alternativa para
__str__.
__eq__: redefinir ==
Por defecto, a == b compara los identificadores de los objetos (is), no
su contenido. Para la comparación estructural, define __eq__:
class Punto:
def __init__(self, x, y):
self.x, self.y = x, y
def __eq__(self, other):
if not isinstance(other, Punto):
return NotImplemented
return (self.x, self.y) == (other.x, other.y)
Punto(1, 2) == Punto(1, 2) # True
Punto(1, 2) == Punto(3, 4) # FalseDevolver NotImplemented (¡una palabra clave especial, no False!) cuando los tipos
no son comparables es la práctica correcta: Python intentará entonces con el
__eq__ del otro operando.
__len__: soporte para len()
class Cesto:
def __init__(self):
self.frutti = []
def __len__(self):
return len(self.frutti)
c = Cesto()
c.frutti.append("mela")
len(c) # 1
bool(c) # True (len > 0)Al definir __len__, también obtienes el booleano gratis: un objeto
con len() == 0 se vuelve falso (falsy).
Otros dunders útiles (resumen)
__add__(self, other)→ redefine+__lt__,__le__,__gt__,__ge__→ operadores de comparación__contains__(self, item)→ soportaitem in self__iter__+__next__→ hace que el objeto sea iterable__getitem__(self, key)→ soportaself[key]__hash__→ necesario para ser clave de diccionario / elemento de conjunto
Pruébalo tú
Define `Money` con __init__(self, amount, currency) y __str__ que devuelva `f'{amount} {currency}'`. Crea `s = Money(42, 'EUR')` y evalúa `str(s)`.
Mostrar pista
f-string en __str__.
Solución disponible después de 3 intentos
Ejercicio de repaso
Define `Pair` con __init__(self, a, b) y __eq__ basado en la comparación estructural de (a, b). Evalúa `Pair(1, 2) == Pair(1, 2)`.
Mostrar pista
Compara las tuplas (self.a, self.b) == (other.a, other.b).
Solución disponible después de 3 intentos
Desafío adicional
Define una clase `Book` con un constructor que tome `title` y `author`. Define el método especial `__str__(self)` para que devuelva la cadena `"Title by Author"` (ej. `'1984 by George Orwell'`). Instancia un libro y evalúa `str(book)`.
Mostrar pista
Usa f"{self.title} by {self.author}" dentro de def __str__(self):.
Solución disponible después de 3 intentos