Что мне нужно сделать, чтобы использовать мои объекты настраиваемого типа в качестве ключей в словаре Python (где я не хочу, чтобы «идентификатор объекта» действовал как ключ), например
class MyThing:
def __init__(self,name,location,length):
self.name = name
self.location = location
self.length = length
Я бы хотел использовать MyThing в качестве ключей, которые считаются одинаковыми, если имя и местоположение совпадают. В C # / Java я привык переопределять и предоставлять метод equals и hashcode, и обещаю не изменять ничего, от чего зависит хэш-код.
Что мне нужно сделать в Python для этого? Я должен даже?
(В простом случае, как здесь, возможно, было бы лучше просто поместить кортеж (имя, местоположение) в качестве ключа, но подумайте, я бы хотел, чтобы ключ был объектом)
python
dictionary
Аноним
источник
источник
MyThing
, чтобы два , если у них одинаковыеname
иlocation
, чтобы индексировать словарь, возвращали одно и то же значение, даже если они были созданы отдельно как два разных «объекта».Ответы:
Вам нужно добавить 2 метода , обратите внимание
__hash__
и__eq__
:class MyThing: def __init__(self,name,location,length): self.name = name self.location = location self.length = length def __hash__(self): return hash((self.name, self.location)) def __eq__(self, other): return (self.name, self.location) == (other.name, other.location) def __ne__(self, other): # Not strictly necessary, but to avoid having both x==y and x!=y # True at the same time return not(self == other)
Документация Python dict определяет эти требования к ключевым объектам, т.е. они должны быть хешируемыми .
источник
hash(self.name)
выглядит лучше, чемself.name.__hash__()
, и если вы это сделаете, вы можетеhash((x, y))
избежать XORing самостоятельно.x.__hash__()
тоже неправильный , потому что он может давать неверные результаты: pastebin.com/C9fSH7eFand
for,__eq__
но потом подумал: «Почему бы не использовать кортежи?» потому что я и так часто делаю это (думаю, это более читабельно). Однако по какой-то странной причине мои глаза не возвращались к вопросу__hash__
.__ne__()
была «фиксированной» .Альтернативой в Python 2.6 или выше является использование
collections.namedtuple()
- это избавляет вас от написания каких-либо специальных методов:from collections import namedtuple MyThingBase = namedtuple("MyThingBase", ["name", "location"]) class MyThing(MyThingBase): def __new__(cls, name, location, length): obj = MyThingBase.__new__(cls, name, location) obj.length = length return obj a = MyThing("a", "here", 10) b = MyThing("a", "here", 20) c = MyThing("c", "there", 10) a == b # True hash(a) == hash(b) # True a == c # False
источник
Вы переопределяете,
__hash__
если вам нужна особая хеш-семантика, и /__cmp__
или__eq__
чтобы сделать ваш класс пригодным для использования в качестве ключа. Объекты, которые сравнивают равные, должны иметь одинаковое хеш-значение.Python ожидает
__hash__
вернуть целое число, возвращатьBanana()
не рекомендуется :)Как вы заметили, определенные пользователем классы
__hash__
по умолчанию имеют этот вызовid(self)
.Существует несколько дополнительных советов от документации :.
источник
__eq__
либо__cmp__
.__cmp__
предоставляется Python, если это определенный пользователем класс, но вы, вероятно, все равно захотите переопределить их, чтобы приспособиться к новой семантике.cmp
и использовать=
классы пользователей, которые не переопределяют эти методы, один из них должен быть реализован, чтобы удовлетворить требование спрашивающего, чтобы экземпляры с аналогичным именем и расположением имели одинаковый ключ словаря.