Tell me more ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

I need objects in python that I can compare, reference to and hash.

In principal a tuple or list would be good enough just that a list I cannot hash and the tuple I cannot change, except replacing it which means losing references to it.

So I created a class for it, but still have the feeling there should be a simpler/better solution to it.

class MyClass:
    def __init__(self, k, l=0, m=0):
        self.k = k
        self.l = l
        self.m = m
    def __hash__(self):
        n_attr = len(self.__dict__)
        return sum(hash(attr)/n_attr for attr in self.__dict__.values())
    def __eq__(self, other):
        return all(sa==oa for sa, oa in zip(self.__dict__.values(),
                                           other.__dict__.values()))

Now I can do what I need: creating some objects

x = MyClass(1,2,3)
y = MyClass(7)
z = MyClass(7,1,0)

and some references

a = x; b = y; c = y; d = z

and compare, manipulate ...

b == c # True
b == d # False
b.l = 1
b == c # still True
b == d # now True
s = {a, d, (a,b)}
b in s # True
c in s # True
(a,c) in s # True

I wished I could just forget about the class and do

x = 1,2,3
y = 7,
z = 7,1

etc ...

Is writing this class the best way to approach this problem? Or did I miss something?

edit:

After the comment by WinstonEwert I confirmed that the objects cause breakage as keys in dictionary

s = {a: 'a', b: 'b'}
s[c] # 'b'
c.l = 99
s[c] # key error
s # key error

and decided to replace dictionaries that use these objects as keys by

class MyDict:
    def __init__(self, d={}):
        self._data = d.items()
    def __getitem__(self, key):
        for k, v in self._data:
            if k == key: return v
        raise KeyError
    def __setitem__(self, key, value):
        for i, (k, v) in enumerate(self._data):
            if k == key:
                self._data[i] = key, value
                return
        self._data.append((key,value))
    def __contains__(self, key):
        for k, v in self._data:
            if k == key: return True
        return False

Now I have the desired behavior

s = MyDict()
s[a] = 'a'
s[b] = 'b'
s[c] # 'b'
c.l = 99
s[c] # still 'b'

And I do not need MyClass any more and can use list

x = [1,2,3]
y = [7,0,0]
z = [7,1,0]
share|improve this question
Why do you want the object to be hashable? – Winston Ewert May 10 at 0:26
currently I'm using the objects as keys in a dictionary – Matthias 009 May 10 at 0:33
That's going to break when you change the object you are using as a key. – Winston Ewert May 10 at 0:37
There are always just 4-5 elements in a dictionary so O(n) lookup would be ok, maybe I could replace the dictionaries by list of tuples and search by comparing. Dictionaries were just so comfortable. – Matthias 009 May 10 at 0:37
@WinstonEwert Seems you are right, there is a problem (extended to question). I was expecting the second s[c] to still give b. I was assuming that the keys in the dictionary are connected to c and change if I change c. – Matthias 009 May 10 at 0:50

1 Answer

up vote 3 down vote accepted

The python dictionary assumes that the keys are immutable. It assumes they will not change. That's why mutable classes in python (such as lists) don't hash. Its to prevent you from putting them in dicts which just won't work.

As a result the entire premise of your class is wrong. You shouldn't do that. To follow what python expects, an object should return the same hash and be equal to the same objects throughout its lifetime. If it can't it shouldn't support hashing.

We might be able to suggest a better way to do this, but you'll need to show more of how you want to use this class.

share|improve this answer
I now agree that I should not use MyClass and now changed the way I'm using it instead (see edit). Thanks for pointing out the problem – Matthias 009 May 10 at 1:57
@Matthias009, the only issue I see is that you'll have slightly odd behavior if you make an object equal to another object after adding them both to your dictionary. – Winston Ewert May 10 at 2:00
Also true. And this behavior will actually cause me problems. I have to give it some more thinking what exactly I want. – Matthias 009 May 10 at 2:45

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.