Я могу использовать изменяемый объект в качестве словарного ключа в python. Разве это не запрещено?
class A(object):
x = 4
i = A()
d = {}
d[i] = 2
print d
i.x = 10
print d
Я думал, что только неизменяемые объекты могут быть словарными клавишами, но объект я выше изменен.
Ответы
Ответ 1
Любой объект с __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
Ответ 2
Требование состоит в том, что хэш объекта не изменяется со временем и что он сравнивает равный (==) с его исходным значением. Ваш класс A отвечает этим требованиям, поэтому он делает действительный ключ словаря. Атрибут x вообще не учитывается при манипуляции ключами, только идентификатор объекта.
Ответ 3
Объект kan является ключом в словаре, если он hashable.
Вот определение хеширования из документации:
Объект hashable, если он имеет значение хэша, которое никогда не изменяется в течение его жизненного цикла (ему нужен метод __hash__()
) и может по сравнению с другими объектами (требуется __eq__()
или __cmp__()
). Объекты Hashable, которые сравниваются равными, должны иметь одно и то же значение хэш-функции.
Hashability позволяет использовать объект как ключ словаря и член набора, поскольку эти структуры данных используют внутреннее значение хэша.
Все неиспользуемые встроенные объекты Pythons являются хешируемыми, в то время как нет изменяемых контейнеров (таких как списки или словари). Объекты, являющиеся экземплярами пользовательских классов, по умолчанию хешируются; все они сравниваются неравномерно, а их хэш-значение - их id().
Так как object
обеспечивает реализацию по умолчанию __hash__
, __eq__
и __cmp__
, это означает, что все, происходящее из object
, является хешируемым, если оно явно не определено как хешируемое. Не разрешается создавать изменяемый тип, который можно использовать для хэширования, но он может не вести себя так, как вы хотите.
Ответ 4
Пример @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 gone!
if myobject not in myset:
print("what the hell we JUST put it in there")