Как определить, является ли объект / экземпляр хэш - PullRequest
2 голосов
/ 24 мая 2019

В последнее время я обнаружил интересное наблюдение, что есть вещи, которые влияют на хеш-свойство объекта / экземпляра класса. И мне интересно как и почему?

Например, я получил класс связанного списка с именем ListNode:

class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None

   def __repr__(self):
        if self.next:
            return "{}->{}".format(self.val, repr(self.next))
        else:
            return "{}".format(self.val)

    # def __eq__(self, other):
        # if not self and not other:
        #     return True
        # elif not self or not other:
        #     return False
        # else:
        #     return self.val == other.val and self.next == other.next

     # def __eq__(self, other):
        # return str(self) == str(other)

Обратите внимание, что я заблокировал метод __eq__. Теперь, если я создам экземпляр:

A = ListNode(1)
B = ListNode(2)
C = ListNode(3)
A.next = B
B.next = C
print(hash(A))

Тогда он может быть хэшируемым, однако я получаю разные выходные значения при каждом запуске.

Теперь, если я разблокирую метод __eq__, внезапно он больше не будет хэшируемым. Зачем?

Похоже, что метод hash будет использовать __eq__. И как он узнает, что он не может быть хешируемым после включения __eq__?

Дополнительно: Если я напишу метод __eq__ для сравнения версии связанного списка str (второй метод __eq__), я подумал, что это может решить проблему, потому что путем преобразования связанного списка в string, данные становятся хешируемыми, но я все равно получаю сообщение об ошибке unhashable 1027 *

Спасибо!

Согласно комментарию @ juanpa.arrivillaga:

__eq__ удалит метод __hash__ по умолчанию, что сделает его недоступным для использования. Поэтому я добавил свой собственный __hash__ метод:

def __hash__(self):
    return hash(id(self))

Это решило проблему и снова сделало ListNode хешируемым с включенным __eq__.

1 Ответ

2 голосов
/ 24 мая 2019

Если класс не определяет метод __eq __ (), он также не должен определять операцию __hash __ ();если он определяет __eq __ (), но не __hash __ (), его экземпляры не будут использоваться как элементы в хешируемых коллекциях.

(...)

Класс, который переопределяет __eq __ () и не определяет __hash __ (), будет иметь для __hash __ () неявно значение None.Когда метод __hash __ () класса имеет значение None, экземпляры класса вызовут соответствующий TypeError, когда программа попытается получить их значение хеш-функции, и будут также правильно определены как не подлежащие обработке при проверке isinstance (obj, collection.abc.Hashable).). 1

Введение метода __eq __ (), следовательно, устанавливает для __hash __ () значение None.Вы можете добавить собственный хеш, чтобы разрешить построение выше:

def __hash__(self):
    return self.val

Более подробную информацию вы можете найти здесь: https://docs.python.org/3/reference/datamodel.html#object.hash

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...