Reputation: 89
I had a question about dictionaries with custom objects. In a dict, I know that the key has to be immutable, so if I want to use a custom class, I have to define its hash function. The hash doc in python recommends you use the hash function on the tuple of the equal dunder method. So for example, i defined the custom class temp as such:
class temp():
def __init__(self,value):
self.value = value
def __hash__(self):
return hash(self.value)
def __eq__(self,other):
return self.value == other.value
def __lt__(self,other):
return self.value < other.value
This way I can have they key:value pair such as temp(1):1. So to my question. In python, you can have different types in the same dict. So I declared this dict:
myDict={ temp(1):1, temp(2):2, 'a':1,1:1, (1,2):1, True:1 }
The problem I am facing is that I would get an error for the int:int and bool:int pairing telling me the error:
'bool' object has no attribute 'value'
or
'int' object has no attribute 'value'
Can someone explain to me why this is the case? The same issue would happen if I have a different class in the dict as well. So an object from a cars class would give this error:
'cars' object has no attribute 'value'
Strangely enough in my tests, I found that if the key is a tuple or a float, it works fine. Any help would be greatly appreciated. I wanted to know why the error is happening and how I can fix it. MY main goal is to learn how to my one dict that has various objects from different classes.
Upvotes: 0
Views: 878
Reputation: 2039
You can define your __eq__
method like this:
def __eq__(self, other):
if other is None:
return False
if self.__class__ != other.__class__:
return False
return self.value == other.value
Strangely enough in my tests, I found that if the key is a tuple or a float, it works fine.
As for the second question, this has got to do with how a dict
works.
For every key, the instance of dict
checks if the hash of the key exists. If yes, then it checks for equality with other keys with the same hash. Here, the check for equality is to check if they are basically the same keys (and hence the same hash). If the equality check fails, then the keys are deemed different.
If there are no hash collisions, then no equality checks are done. Hence, when you used a tuple
as a key, say, (1, 2)
, its hash((1, 2)) = 3713081631934410656
, which doesn't yet exist in the dict
. Hence no error.
Upvotes: 1
Reputation: 8594
Your eq method needs to check if the other object is the same type:
def __eq__(self,other):
if not isinstance(other, temp):
return NotImplemented
return self.value==other.value
That said, I highly recommend using dataclasses for cases like this. They define init, eq, and (if frozen=True
) hash for you, which helps avoid this sort of issue.
Upvotes: 2
Reputation: 2139
The issue happens when running the __eq__
and __lt__
dunder methods. You can reproduce the same by running:
temp(1) == 1
The issue happens because __eq__
receives other
as 1, and the value 1 does not have a .value
, but you're trying to use it here:
return self.value == other.value
If you just use other for comparisons it should work:
class temp():
def __init__(self,value):
self.value = value
def __hash__(self):
return hash(self.value)
def __eq__(self,other):
return self.value == other
def __lt__(self,other):
return self.value < other
Upvotes: 1