объекты как ключи в словарях python - PullRequest
14 голосов
/ 09 февраля 2011

Я пытаюсь использовать объект в качестве ключа в словаре Python, но он ведет себя таким образом, что я не совсем понимаю.

Сначала я создаю словарь с моим объектом в качестве ключа:

package_disseminators = {
  ContentType("application", "zip", "http://other/property") : "one",
  ContentType("application", "zip") : "two"
}

Теперь создайте еще один объект, который "совпадает" с тем, который является ключом.

content_type = ContentType("application", "zip", "http://other/property")

Я дал объекту ContentType пользовательские __eq__ и пользовательские __str__ методы, так что метод __eq__ сравнивает значения __str__.

Теперь немного интерактивного питона:

>>> for key in package_disseminators:
...     if key == content_type:
...             print "match"
...     else:
...             print "no match"
... 
no match
match

>>> content_type in package_disseminators.keys()
True

Хорошо, похоже, мой объект определенно идентифицируется как ключ, поэтому:

>>> package_disseminators[content_type]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: (& (type="application/zip") (packaging="http://other/property") )

Э-э ... хорошо? Значит, content_type есть в списке package_disseminators.keys (), но не является ключом?

>>> package_disseminators.has_key(content_type)
False

Видимо, нет.

Я полагаю, что процесс сравнения, который Python использует для определения равенства, отличается между прямым утверждением «в» в списке и фактическим поиском ключа в диктовке, но я не знаю как. Любые советы или идеи?

Ответы [ 2 ]

25 голосов
/ 09 февраля 2011

Из документации питона:

Ключи словаря почти произвольные значения. Значения, которые не являются hashable, то есть значения, содержащие списки, словари или другие изменяемые типы (которые сравниваются по значению а не по идентичности объекта) может не должен использоваться в качестве ключей.

Hashable определяется следующим образом

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

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

Так что, если вы хотите сделать это, вам необходимо переопределить метод __hash__() по умолчанию для вашего объекта (см. Комментарий Стивена Румбальски ниже для более подробного объяснения).


>>> content_type in package_disseminators.keys()
True

Полагаю, это работает, потому что dict.keys() возвращает список, а __contains__, вероятно, проверяет равенство, но не для тех же хешей.

18 голосов
/ 09 февраля 2011

Поскольку dicts - это хеш-таблицы под капотом, вам нужно определить и __eq__, и __hash__, чтобы это работало.

Основное правило:

  • Для объектов, которые __eq__ сравниваются равными, __hash__ должен возвращать тот же хеш.

Из вашего описания что-то вроде

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

должно работать.

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