__eq__ и __ne__ позволяют определить пользовательскую логику сравнения объектов. Для сложных структур данных, таких как классы с несколькими атрибутами или вложенные структуры, можно переопределить эти методы для сравнения по значениям конкретных атрибутов или по рекурсивному сравнению вложенных структур. Например, можно сравнивать объекты класса по идентичности их ключевых атрибутов или реализовать глубокое сравнение списков и словарей, рекурсивно вызывая __eq__ для каждого элемента.  Переопределение __eq__ автоматически включает поведение для __ne__ (отрицание результата __eq__), если __ne__ не определен явно.
Методы __eq__ (равно) и __ne__ (не равно) являются "магическими" методами в Python, которые позволяют переопределить стандартное поведение операторов == и != для экземпляров пользовательских классов. Это особенно полезно при сравнении сложных структур данных, таких как списки, словари, множества, или пользовательские объекты, где простое сравнение по идентификатору (как это происходит по умолчанию) недостаточно.
Как использовать:
При создании пользовательского класса, можно определить методы __eq__ и __ne__.  Метод __eq__ должен возвращать True, если два объекта считаются равными, и False в противном случае.  Метод __ne__ должен возвращать True, если два объекта считаются неравными, и False в противном случае.
Пример:
  class Point:
      def __init__(self, x, y):
          self.x = x
          self.y = y
      def __eq__(self, other):
          if isinstance(other, Point):
              return self.x == other.x and self.y == other.y
          return False  # Сравнение с объектом другого типа всегда False
      def __ne__(self, other):
          return not self.__eq__(other) # Оптимально использовать __eq__
  #Примеры использования
  p1 = Point(1, 2)
  p2 = Point(1, 2)
  p3 = Point(3, 4)
  print(p1 == p2)  # Выведет: True
  print(p1 == p3)  # Выведет: False
  print(p1 != p2)  # Выведет: False
  print(p1 != p3)  # Выведет: True
  print(p1 == "hello") # Выведет False
  Объяснение примера:
Point, представляющий точку на плоскости.__eq__ сравнивает два экземпляра Point по значениям их атрибутов x и y.  Если оба атрибута равны, метод возвращает True, иначе - False. Важно проверять тип `other` через `isinstance`, чтобы избежать ошибок при сравнении с другими типами данных. Если тип не `Point`, возвращается `False`.__ne__ просто возвращает логическое отрицание результата метода __eq__. Это рекомендуемый подход, так как достаточно определить только __eq__, а __ne__ можно получить автоматически.Преимущества использования __eq__ и __ne__:
== и != делает код более понятным и лаконичным, чем использование сложных условных конструкций.Важные замечания:
__eq__, Python автоматически создаст __ne__, возвращающий противоположное значение. Однако, если необходимо определить нестандартное поведение для __ne__ (что бывает редко), то его следует переопределить явно.Пример для более сложной структуры:
  class Rectangle:
      def __init__(self, width, height):
          self.width = width
          self.height = height
      def __eq__(self, other):
          if isinstance(other, Rectangle):
              return self.width * self.height == other.width * other.height
          return False
      def __ne__(self, other):
          return not self.__eq__(other)
  rect1 = Rectangle(5, 10)
  rect2 = Rectangle(10, 5)
  rect3 = Rectangle(2, 3)
  print(rect1 == rect2) # True (площади равны)
  print(rect1 != rect3) # True (площади не равны)
  В этом примере сравниваются прямоугольники по их площади. Это демонстрирует, как __eq__ позволяет реализовать сравнение на основе логики, специфичной для класса.