Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I make my classes usable as dict keys?

class A():
   def __init__(self, data=''):
       self.data = data  

   def __str__(self):
       return str(self.data)

d = {}  
elem = A()  
d[elem] = 'abc'  

elem2 = A()
print d[elem2]    # KeyError  
# actually elem2! was used not elem

how can I implement this without error?

I tried to get d[elem2] (not elem) with another instance of A() BUT with the same content.

like image 777
Sergey Avatar asked Mar 07 '11 15:03

Sergey


People also ask

Can a class be a key in dictionary?

Yes, only when getting, not setting. Edited: KeyError. @Sergey: You will have to tell us more about class A . Did you overwrite any special methods?

Can classes be used as dictionary keys Python?

This describes possible ways of using userdefined class instances as dictionary keys. Sometimes one wants to use instances of userdefined classes as dictionary keys. For this to be possible, the user supplied class must provide a __hash__ method. But there some other issues for this to work reliably: 1.)

Can a class be a dictionary in Python?

When it is required to form a dictionary with the help of an object and class, a class is defined. An 'init' function is defined, that assigns values to variables. An instance of the class is created, and the init function is called.

Can you use an object as a dictionary key?

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.


2 Answers

The answer is yes, you need to redefine __hash__() and __eq__():

>>> class A(object):
...   def __init__(self, data=''):
...     self.data = data
...   def __eq__(self, another):
...     return hasattr(another, 'data') and self.data == another.data
...   def __hash__(self):
...     return hash(self.data)
... 
>>> a1, a2, a3 = A('foo'), A('foo'), A('bar')
>>> d = {a1: 'foo'}
>>> d[a1]
'foo'
>>> d[a2]
'foo'
>>> d[a3]
Traceback (most recent call last):
  File "", line 1, in 
KeyError: __main__.A object at 0x927d0>

As explained in another comment default implementation of __hash__ is just simple identity, so if you want to make it more sophisticated, you need to define it explicitly.

like image 182
Michal Chruszcz Avatar answered Oct 03 '22 06:10

Michal Chruszcz


What you did should work, as long as you don't override the __hash__() and __eq__() methods. It will use object identity as equality. If you want a different notion of equality, you can override the __hash__() and __eq__() methods of your class.

like image 29
Sven Marnach Avatar answered Oct 03 '22 05:10

Sven Marnach