Я могу использовать изменяемый объект в качестве словарного ключа в Python.Разве это не запрещено? - PullRequest
16 голосов
/ 11 декабря 2010
class A(object):
    x = 4

i = A()
d = {}

d[i] = 2

print d

i.x = 10

print d

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

Ответы [ 4 ]

20 голосов
/ 11 декабря 2010

Любой объект с методом __ hash __ может быть ключом словаря.Для классов, которые вы пишете, этот метод по умолчанию возвращает значение, основанное на id (self), и если равенство не определяется идентичностью для этих классов, вы можете быть удивлены использованием их в качестве ключей:

>>> class A(object):
...   def __eq__(self, other):
...     return True
... 
>>> one, two = A(), A()
>>> d = {one: "one"}
>>> one == two
True
>>> d[one]
'one'
>>> d[two]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: <__main__.A object at 0xb718836c>

>>> hash(set())  # sets cannot be dict keys
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'set'

Изменено в версии 2.6: теперь __hash__ может быть установлен в None, чтобы явно помечать экземпляры класса как не подлежащие обработке. [ __ hash __ ]

class Unhashable(object):
  __hash__ = None
6 голосов
/ 11 декабря 2010

Объект может быть ключом в словаре, если он hashable .

Вот определение hashable из документации:

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

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

Поскольку object обеспечивает реализацию по умолчанию __hash__, __eq__ и __cmp__, это означает, что все, что происходит отobject является хэшируемым, если явно не определено, что он хэшируемый.Не разрешается создавать изменяемый тип, который можно хэшировать, но он может вести себя не так, как вы хотите.

6 голосов
/ 11 декабря 2010

Требуется, чтобы хэш объекта не менялся со временем, и чтобы он продолжал сравнивать равный (==) с его исходным значением.Ваш класс А отвечает обоим этим требованиям, поэтому он является допустимым ключом словаря.Атрибут x вообще не учитывается при вводе ключа, только идентификатор объекта.

4 голосов
/ 07 февраля 2016
К счастью, приведенный выше пример

@ fred-nurk больше не работает в Python 3 из-за этого изменения :

Класс, который переопределяет __eq__() и не определяет __hash__(), будет иметь __hash__(), неявно установленный на None. Когда __hash__() метод класса равен None, экземпляры класса вызовут соответствующий TypeError, когда программа попытается получить их хэш-значение ...

Слава Богу за это. Однако, если вы явно определите __hash__() для себя, вы все равно можете совершать злые поступки:

class BadHasher:
    def __init__(self):
        self.first = True

    # Implement __hash__ in an evil way. The first time an instance is hashed,
    # return 1. Every time after that, return 0.
    def __hash__(self):
        if self.first:
            self.first = False
            return 1
        return 0

myobject = BadHasher()
# We can put this object in a set...
myset = {myobject}
# ...but as soon as we look for it, it's gone!
if myobject not in myset:
    print("what the hell we JUST put it in there")
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...