What must I do to use my objects of a custom type as keys in a Python dictionary (where I don't want the "object id" to act as the key) , e.g.
class MyThing: def __init__(self,name,location,length): self.name = name self.location = location self.length = length
I'd want to use MyThing's as keys that are considered the same if name and location are the same. From C#/Java I'm used to having to override and provide an equals and hashcode method, and promise not to mutate anything the hashcode depends on.
What must I do in Python to accomplish this ? Should I even ?
(In a simple case, like here, perhaps it'd be better to just place a (name,location) tuple as key - but consider I'd want the key to be an object)
Dictionaries in Python Almost any type of value can be used as a dictionary key in Python. You can even use built-in objects like types and functions. However, there are a couple restrictions that dictionary keys must abide by. First, a given key can appear in a dictionary only once.
Properties of Dictionary KeysThey can be any arbitrary Python object, either standard objects or user-defined objects. However, same is not true for the keys.
Any type that conforms to the Hashable protocol can be used as a dictionary's Key type, including all of Swift's basic types. You can use your own custom types as dictionary keys by making them conform to the Hashable protocol.
The keys in a dictionary can be a reference type, i.e., objects.
You need to add 2 methods, note __hash__
and __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)
The Python dict documentation defines these requirements on key objects, i.e. they must be hashable.
An alternative in Python 2.6 or above is to use collections.namedtuple()
-- it saves you writing any special methods:
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With