Почему наборы Python не могут быть хэшируемыми? - PullRequest
58 голосов
/ 10 июня 2011

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

>>> set([ set() ])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'set'

Есть ли веская причина, по которой наборы Python не являются хэшируемыми?

Ответы [ 4 ]

103 голосов
/ 10 июня 2011

Как правило, только неизменяемые объекты являются хэшируемыми в Python. Неизменяемый вариант set() - frozenset() - можно изменить.

27 голосов
/ 10 июня 2011

Потому что они изменчивы.

Если бы они были хэшируемыми, хэш мог бы молча стать "недействительным", и это в значительной степени сделало бы хеширование бессмысленным.

16 голосов
/ 10 июня 2011

Из документов Python:

hashable
Объект является хэшируемым, если он имеет значение хеша, которое никогда не меняется в течение своей жизни (ему нужно hash ()), и его можно сравнить с другими объектами (для этого требуется eq () или cmp ()). Хешируемые объекты, которые сравниваются равными должен иметь одинаковое хеш-значение.

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

Все неизменные встроенные в Python объекты могут быть хэшируемыми контейнеры (такие как списки или словари) есть. Объекты, которые экземпляры пользовательских классов Хешируемый по умолчанию; они все сравнивают неравенство, и их хэш-значение является их Идентификатор ().

6 голосов
/ 25 февраля 2014

В случае, если это помогает ... если вам действительно нужно по какой-то причине преобразовать не подлежащие обработке вещи в хешируемые эквиваленты, вы можете сделать что-то вроде этого:

from collections import Hashable, MutableSet, MutableSequence, MutableMapping

def make_hashdict(value):
    """
    Inspired by /1297660/python-hashable-dicts
     - with the added bonus that it inherits from the dict type of value
       so OrderedDict's maintain their order and other subclasses of dict() maintain their attributes
    """
    map_type = type(value)

    class HashableDict(map_type):
        def __init__(self, *args, **kwargs):
            super(HashableDict, self).__init__(*args, **kwargs)
        def __hash__(self):
            return hash(tuple(sorted(self.items())))

    hashDict = HashableDict(value)

    return hashDict


def make_hashable(value):
    if not isinstance(value, Hashable):
        if isinstance(value, MutableSet):
            value = frozenset(value)
        elif isinstance(value, MutableSequence):
            value = tuple(value)
        elif isinstance(value, MutableMapping):
            value = make_hashdict(value)

        return value

my_set = set()
my_set.add(make_hashable(['a', 'list']))
my_set.add(make_hashable({'a': 1, 'dict': 2}))
my_set.add(make_hashable({'a', 'new', 'set'}))

print my_set

Моя реализация HashableDict - самый простой и наименее строгий примерот здесь .Если вам нужен более продвинутый HashableDict, который поддерживает травление и другие вещи, проверьте множество других реализаций.В моей версии выше я хотел сохранить исходный класс dict, сохраняя порядок OrderedDicts.Я также использую AttrDict из здесь для доступа, подобного атрибуту.

Мой пример выше ни в коем случае не является авторитетным, просто мое решение аналогичной проблемы, где мне нужно было хранить некоторые вещи вустановить и нужно сначала их "хешировать".

...