Как словарь ищет ключ: вычисляет hash(key), по нему находит ячейку, а при совпадении хешей сравнивает через ==. Если равные объекты дают разные хеши, словарь их не найдёт.
Почему изменяемые нехешируемы. Если бы список был ключом и потом изменился, его хеш изменился бы — запись осталась бы в ячейке старого хеша и стала недоступной.
class Point:
def __init__(self, x, y): self.x, self.y = x, y
def __eq__(self, other):
return isinstance(other, Point) and (self.x, self.y) == (other.x, other.y)
def __hash__(self):
return hash((self.x, self.y))
По умолчанию объекты сравниваются по идентичности и хешируются по id — это согласовано. Переопределили __eq__ — Python обнулил __hash__, чтобы не допустить несогласованности; нужно определить его явно.
Возвращать NotImplemented из __eq__ для чужих типов — тогда Python попробует сравнение с другой стороны.