Таблица поиска для unhashable в Python

мне нужно создать сопоставление из объектов моего собственного пользовательского класса (производного от dict) с объектами другого пользовательского класса. Как я вижу, есть два способа сделать это:--3-->

  1. Я могу сделать объекты hashable. Не знаю, как бы я это сделал. Я знаю, что могу реализовать __hash__() но я не уверен, как на самом деле вычислить хэш (который должен быть целым числом).

  2. поскольку мои объекты можно сравнить, я могу сделать список [(myobj, myotherobj)] и затем реализуйте поиск, который находит кортеж, где первый элемент кортежа совпадает с ключом поиска. Реализация этого тривиальна (количество объектов невелико), но я хочу избежать изобретения колеса, если что-то подобное уже существует в стандартной библиотеке.

мне кажется, что желание искать unhashables было бы общей проблемой, поэтому я предполагаю, что кто-то уже решил эту проблему. Любые предложения о том, как реализовать __hash()__ для a dict-подобный объект или если есть какой-то другой стандартный способ создания таблиц поиска unhashables?

4 ответов


сопоставления с изменяемыми объектами в качестве ключей обычно сложны. Ты действительно этого хочешь? Если вы считаете, что ваши объекты неизменяемы (в Python нет способа действительно обеспечить неизменяемость), или вы знаете, что они не будут изменены, пока они используются в качестве ключей в сопоставлении, вы можете реализовать свою собственную хэш-функцию для них несколькими способами. Например, если ваш объект имеет только хэшируемые члены данных, вы можете вернуть хэш кортежа всех членов данных в качестве объектов хэш.

Если ваш объект похож на dict, вы можете использовать хэш frozenset всех пар ключ-значение.

def __hash__(self):
    return hash(frozenset(self.iteritems()))

это работает только если все значения hashable. Чтобы сохранить пересчеты хэшей (которые будут выполняться при каждом поиске), вы можете кэшировать хэш-значение и просто пересчитать его, если установлен какой-то грязный флаг.


простым решением, похоже, является do lookup[id(myobj)] = myotherobj вместо lookup[myobj] = myotherobj. Есть согласие на такой подход?


следующее должно работать, если вы не храните дополнительные объекты unhashable в своем пользовательском классе:

def __hash__(self):
    return hash(self.items())

вот реализация frozendict, взято из http://code.activestate.com/recipes/414283/:

class frozendict(dict):
    def _blocked_attribute(obj):
        raise AttributeError, "A frozendict cannot be modified."
    _blocked_attribute = property(_blocked_attribute)

    __delitem__ = __setitem__ = clear = _blocked_attribute
    pop = popitem = setdefault = update = _blocked_attribute

    def __new__(cls, *args):
        new = dict.__new__(cls)
        dict.__init__(new, *args)
        return new

    def __init__(self, *args):
        pass

    def __hash__(self):
        try:
            return self._cached_hash
        except AttributeError:
            h = self._cached_hash = hash(tuple(sorted(self.items())))
            return h

    def __repr__(self):
        return "frozendict(%s)" % dict.__repr__(self)

Я бы заменил tuple(sorted(self.items())) by frozenset(self.iteritems()) а в Spacecowboy это. И подумайте о добавлении __slots__ = ("_cached_hash",) в класс.