ProgramaciónDesarrollador Python medio

¿Cómo se determina el funcionamiento del operador de comparación '==' y el método __eq__ en Python? ¿Cómo se puede modificar el comportamiento de la comparación para nuestras propias clases?

Supere entrevistas con el asistente de IA Hintsage

Respuesta.

Historia de la pregunta:

En Python, la comparación de objetos por valor se realiza a través del operador '==', que internamente llama al método eq. El comportamiento de este método por defecto se hereda de object y compara la identidad de los objetos (para la mayoría de las clases).

Problema:

Cuando queremos que las instancias de las clases personalizadas se comparen de manera significativa (por ejemplo, que dos objetos diferentes de Person con el mismo conjunto de datos sean considerados iguales), el comportamiento estándar no es suficiente: es necesario definir explícitamente cómo se realiza la comparación.

Solución:

Para cambiar el comportamiento de '==', es necesario sobrescribir el método eq en nuestra propia clase. Si la clase debe ser hashable, también será necesario sobrescribir hash.

Ejemplo de código:

class Person: def __init__(self, name, age): self.name = name self.age = age def __eq__(self, other): if not isinstance(other, Person): return NotImplemented return self.name == other.name and self.age == other.age p1 = Person("Ann", 25) p2 = Person("Ann", 25) print(p1 == p2) # True

Características clave:

  • '==' llama a eq.
  • Al comparar objetos sin eq, se comparará su identidad (id).
  • Para un correcto funcionamiento con conjuntos/diccionarios, también es necesario implementar hash.

Preguntas complicadas.

¿Es la comparación '==' siempre simétrica para objetos de diferentes tipos?

No necesariamente: si el primer objeto devuelve NotImplemented, se llama a la comparación inversa. De lo contrario, puede haber asimetría.

class A: def __eq__(self, other): return True class B: pass print(A() == B()) # True print(B() == A()) # False

Si no se implementa eq, ¿cómo se compararán los objetos de una misma clase personalizada?

Se comparará la identidad (id en memoria, no los valores de los atributos).

class Foo: def __init__(self, x): self.x = x f1 = Foo(5) f2 = Foo(5) print(f1 == f2) # False

¿Se puede definir eq sin hash para objetos que deben ser claves de un diccionario?

No. Si eq se modifica, también es deseable definir hash, de lo contrario, el objeto se volverá no hashable (TypeError al usarlo como clave).

class Foo: def __eq__(self, other): return True # hash se hereda de object, pero el comportamiento será impredecible # Mejor definir explícitamente __hash__

Errores comunes y anti-patrones

  • Implementar eq sin hash donde se necesitan claves en dict/set.
  • Error al comparar con objetos de otro tipo (debe devolver NotImplemented, no False).
  • Definición inconsistente de eq y hash, que lleva a un comportamiento incorrecto en estructuras hash.

Ejemplo de la vida real

Caso negativo

Al diseñar la clase ValueObject en el equipo, no se tuvo en cuenta que solo se implementó eq, pero no hash. Al intentar usar el objeto como clave en un diccionario o elemento de un conjunto, se produjo una excepción TypeError.

Ventajas:

Rápida implementación de comparación personalizada.

Desventajas:

Imposibilidad de utilizar objetos en conjuntos y diccionarios, dificultad para depurar.

Caso positivo

En otro proyecto, los desarrolladores implementaron claramente ambos métodos: eq y hash, siguiendo rigurosamente la regla: si dos objetos son iguales según eq, sus hashes deben coincidir.

Ventajas:

Funcionamiento correcto de las estructuras hash, comportamiento consistente, facilidad de mantenimiento.

Desventajas:

Pérdida de flexibilidad: los objetos iguales por valor son indistinguibles en contenedores hash.