Проблема с хэш-функцией: hash (1) == hash (1.0) - PullRequest
1 голос
/ 04 августа 2009

У меня есть экземпляр dict с int s, float s, string s в качестве ключей, но проблема в том, что a как int и b как float и float(a) == b, тогда их значения хеш-функции одинаковы, и это то, что я НЕ хочу получить, потому что мне нужны уникальные значения хеш-функции для этих случаев, чтобы получить соответствующие значения.

Пример:

d = {1:'1', 1.0:'1.0', '1':1, '1.0':1.0}
d[1] == '1.0'
d[1.0] == '1.0'
d['1'] == 1
d['1.0'] == 1.0

Что мне нужно, это:

d = {1:'1', 1.0:'1.0', '1':1, '1.0':1.0}
d[1] == '1'
d[1.0] == '1.0'
d['1'] == 1
d['1.0'] == 1.0

Ответы [ 4 ]

7 голосов
/ 04 августа 2009

Поскольку 1 == 1.0, это ужасно нарушило бы семантику хеширования (и, следовательно, диктов и множеств), если бы это было так, что hash(1) != hash(1.0). В более общем смысле, ВСЕГДА должно быть так, что x == y подразумевает hash(x) == hash(y), для ВСЕХ x и y (конечно, нет условий, требующих сохранения обратного значения).

Таким образом, ваш dict d содержит всего три записи, поскольку вторая, которую вы написали на дисплее dict, переопределяет первую. Если вам нужно, чтобы равенство поддерживалось только между одинаковыми типами (в отличие от чисел в более общем смысле), вам нужна оболочка, такая как:

class W(object):

  def __init__(self, x):
    self.x = x
    self.t = type(x)

  def __eq__(self, other):
    t = type(other)
    if t != type(self):
      return False
    return self.x == other.x and self.t == other.t

  def __hash__(self):
    return hash(self.x) ^ hash(self.t)

  def __getattr__(self, name):
    return getattr(self.x, name)

В зависимости от ваших точных потребностей вы также можете переопределить другие методы (другие методы сравнения, такие как __cmp__ или __le__, арифметические, __repr__ и т. Д. И т. Д.). В любом случае, это позволит вам создать диктовку, аналогичную той, которая вам требуется, просто используйте в качестве ключей W(1) вместо голых 1 и W(1.0) вместо голых 1.0 (вам может не понадобиться переносить числа, хотя это не повредит, если вы решите это сделать, и это может облегчить извлечение из вашего запроса, если все ключи одинаково упакованы).

6 голосов
/ 04 августа 2009

Использование числа с плавающей запятой в качестве словарного ключа «неразумно», невозможно гарантировать, что два числа с плавающей запятой будут иметь одинаковое значение.

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

edit: Извините, кажется, вам не нужен дикт с действительными цифровыми клавишами, вы просто хотите отформатировать вывод в зависимости от типа ввода?

2 голосов
/ 04 августа 2009

Если вам действительно нужно знать разницу, возможно, сделайте что-нибудь хакерское, например:

x = '1'
y = 1

hash(type(x) + x) != hash(type(y) + y)
1 голос
/ 04 августа 2009

Это не решит вашу проблему, но из документации по номеру Python 2.6 :

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

Можете ли вы сделать поплавок 1.00001 или что-то в этом роде?

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