Использование id объекта () в качестве хеш-значения - PullRequest
3 голосов
/ 10 марта 2019

это плохая идея для реализации __hash__, как это?

class XYZ:
    def __init__(self):
        self.val = None

    def __hash__(self):
        return id(self)

Я настраиваю что-то потенциально катастрофическое?

Ответы [ 3 ]

4 голосов
/ 10 марта 2019

Для работы __hash__ метод должен удовлетворять следующему требованию:

Так x * y, что x == y, затем hash(x) == hash(y).

В вашем случае ваш класс не реализует __eq__, что означает, что x == y тогда и только тогда, когда id(x) == id(y), и, следовательно, ваша хеш-реализация удовлетворяет вышеуказанному свойству.

Обратите внимание, однако, что если вы делаете внедрите __eq__, то эта реализация, скорее всего, завершится неудачей.

Кроме того: существует разница между наличием «действительного» * ​​1018 * и наличием хорошего хеша. Например, следующее является допустимым __hash__ определением для любого класса:

def __hash__(self):
    return 1

Хороший хеш должен пытаться распределить объекты равномерно, чтобы избежать коллизий в максимально возможной степени. Обычно это требует более сложного определения. Я бы не пытался придумывать формулы и полагаться на встроенную в Python функцию hash.

Например, если в вашем классе есть поля a, b и c, я бы использовал что-то вроде этого __hash__:

def __hash__(self):
    return hash((self.a, self.b, self.c))

Определение hash для кортежей должно быть достаточно хорошим для среднего случая.

Наконец: вы не должны определять __hash__ в изменчивых классах (в полях, используемых для равенства). Это потому, что изменение экземпляров изменит их хэш, и это сломает вещи.

3 голосов
/ 10 марта 2019

Это либо бессмысленно, либо неправильно, в зависимости от остальной части класса.

Если ваши объекты используют основанный на идентификации по умолчанию ==, то определение этого __hash__ бессмысленно.Значение по умолчанию __hash__ также основано на идентичности, но быстрее и настраивается, чтобы избежать установки всегда младших битов в 0. Использование значения по умолчанию __hash__ было бы проще и эффективнее.

Если вы возражаете противЕсли вы не используете == по умолчанию, то ваш __hash__ неверен, потому что он будет несовместим с ==.Если ваши объекты неизменны, вы должны реализовать __hash__ таким образом, чтобы это соответствовало ==;если ваши объекты изменчивы, вы вообще не должны реализовывать __hash__ (и устанавливать __hash__ = None, если вам нужно поддерживать Python 2).

1 голос
/ 10 марта 2019

Это реализация по умолчанию __hash__. Имейте в виду, что реализация __eq__ приводит к исчезновению реализации __hash__ по умолчанию. Если вы переопределите __hash__, то любые объекты, которые сравниваются равными , должны иметь одинаковый хеш.

Вполне допустимо, чтобы неравные объекты имели одинаковое значение хеш-функции. Следовательно, иметь хеш-реализацию, которая возвращает постоянное значение, всегда безопасно. Однако это очень неэффективно.

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

class XYZ:
    def __init__(self, val0, val1):
        self.val0 = val0
        self.val1 = val1

    def __eq__(self, other):
        return self.val0 == other.val1 and self.val1 == other.val1

    def __hash__(self):
        return hash((self.val0, self.val1))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...