Reputation: 180
I want to build a subclass of a class which is basically a wrapper around a list/dictionary, and I want to use instances of this class as keys. In order to have easy access to the list/dictionary methods, I figured I can just subclass them, but then I lose hashability.
I understand that this is incompatible with the __eq__
of the mutable classes (and I don't need it), so I came up with the following solution.
class Foo(list):
__hash__ = object.__hash__
__eq__ = object.__eq__
Though I doubt this will come up in my specific applications, I guess this might cause problems if another class (with its own __hash__
) would appear between Foo
and list
in the MRO of a subclass.
Foo
and list
in MRO)?Upvotes: 0
Views: 105
Reputation: 1841
I decided a provide an answer based on my comment, with a lot if IFs
As stated, I would rather change the implementation of the dict, to take a list
as a key. This however does come with problems, that as others mentioned, is the reason why lists
SHOULDN'T be keys.
A little explanation:
When looking at a mutable variable, it comes with two properties, that it can be compared by:
identity
content
Identity
When a variable is created such as a=[1,2,3]
it is allocated a memory position which can be called with id(a)
. This id identifies a
even after a
is modified. However with a second variable b=[1,2,3]
when comparing ids
we get id(a) == id(b) = False
Content
We can also look at the content of a variable in a very simple way by converting it to a String, so with above example str(a) == str(b) = True
. However if we do
a = [1,2,3]
str_a = str(a)
a.append(4)
str_new_a = str(a)
we get str_a == str_new_a = False
, which is problematic in the case of application as a key in a dict, because at the moment of saving the value, we take a 'snapshot' of a
and later if we lookup by a
after it changed, it wont match.
Therefore you will have to decide, if in your case content or identity is the relevant lookup criterion.
An example implementation with hashableKey1 as an ID match and hashableKey2 as a content mach is provided below.
from collections import Hashable
class CoolDict(dict):
hashableKeyType = 1
def __setitem__(self, name, value):
print(name, value)
if isinstance(name, Hashable):
super().__setitem__(name, value)
else:
super(CoolDict, self).__setitem__(self.hashableKey(name), value)
def __getitem__(self, name):
if isinstance(name, Hashable):
return super().__getitem__(name)
else:
return super().__getitem__(self.hashableKey(name))
def hashableKey(self, name):
if self.hashableKeyType == 1:
return self.hashableKey1(name)
elif self.hashableKeyType == 2:
return self.hashableKey2(name)
def hashableKey1(self, name):
return id(name)
def hashableKey2(self, name):
return str(name)
coolDict = CoolDict()
### hashableKey1:
#Working
key = [1,2,3]
coolDict[key] = 123
print(coolDict[key])
#Not Working
coolDict[[2,3,4]] = 234
print(coolDict[[2,3,4]])
### hashableKey2:
coolDict.hashableKeyType = 2
#Working
coolDict[[2,3,4]] = 234
print(coolDict[[2,3,4]])
#Not Working
key = [1,2,3]
coolDict[key] = 123
key.append(4)
print(key)
print(coolDict[key])
Upvotes: 1