Los métodos mágicos (o métodos dunder – de double underscore) son métodos especiales cuyos nombres comienzan y terminan con dos guiones bajos, por ejemplo, __init__, __str__, __add__. Permiten integrar instancias de clases propias en la sintaxis y el comportamiento de Python: definir la reacción a operaciones aritméticas, comparaciones, conversión a cadenas, trabajar con protocolos de colecciones, etc.
Ejemplo:
class Vector: def __init__(self, x, y): self.x = x self.y = y def __add__(self, other): return Vector(self.x + other.x, self.y + other.y) def __str__(self): return f"Vector({self.x}, {self.y})" v1 = Vector(1, 2) v2 = Vector(3, 4) print(v1 + v2) # Llamará a v1.__add__(v2), devolverá Vector(4, 6)
Si no se implementa
__eq__en la propia clase, ¿cómo se compararán sus instancias entre sí con el operador==?
Respuesta: De manera predeterminada, el operador == compara objetos por identidad (dirección en memoria, similar al operador is), a menos que se redefina el método mágico __eq__.
class A: pass print(A() == A()) # False, aunque los objetos "parecen iguales"
Historia
En un proyecto donde era necesario comparar estructuras de grafos, no se implementó el método __eq__. Como resultado, al comprobar "si ya se había añadido un nodo", se obtenía un resultado incorrecto, ya que el operador == comparaba objetos por id, no por contenido.
Historia
Al escribir una API REST, serializaron un objeto en una cadena para el registro a través de str(obj), pero olvidaron definir __str__. Como resultado, se mostraba un texto ilegible <MyObj object at 0x...>, lo que dificultaba el diagnóstico de problemas.
Historia
En una biblioteca para cálculos matemáticos, se implementó solo __add__, pero se olvidó de __iadd__ para el operador +=. Como consecuencia, la expresión v += w no funcionaba como se esperaba (se creaba un nuevo objeto, no se actualizaba el viejo), lo que llevó a filtraciones de memoria en cálculos complejos.