views:

95

answers:

3

So I have a square that's made up of a series of points. At every point there is a corresponding value.

What I want to do is build a dictionary like this:

class Point:
    def __init__(self, x, y):
        self._x = x
        self._y = y


square = {}    
for x in range(0, 5):
        for y in range(0, 5):
            point = Point(x,y)
            square[point] = None

However, if I later create a new point object and try to access the value of the dictionary with the key of that point it doesn't work..

>> square[Point(2,2)]
Traceback (most recent call last):
  File "<pyshell#19>", line 1, in <module>
    square[Point(2,2)]
KeyError: <__main__.Point instance at 0x02E6C378>

I'm guessing that this is because python doesn't consider two objects with the same properties to be the same object? Is there any way around this? Thanks

+4  A: 

Define Point.__hash__() and Point.__eq__() so that they can be compared properly within dicts.

And while you're at it, consider defining Point.__repr__() so that you get decent-looking representations of your Point objects.

Ignacio Vazquez-Abrams
Thanks! Good tip about __repr__ but I'm a little bit confused about the difference between it and __str__?
Sam
`__repr__()` is used whenever you need a representation of the object, without actually wanting the string value, e.g. within the REPL.
Ignacio Vazquez-Abrams
`__repr__` should also return valid python, that could be passed as an arg to `eval` to create an object identical to the one being repr'd. It should return the following string: `'Point(%s, %s)' % (self._x, self._y)`
Brendan Abel
+2  A: 

Yes, define the __eq__ and __hash__ methods on your Point class.

class Point:
    def __init__(self, x, y):
        self._x = x
        self._y = y

    def __eq__(self, other):
        return self._x == other._x and self._y == other._y

    def __hash__(self):
        #This one's up to you, but it should be unique. Something like x*1000000 + y.
David
Thanks for the code breakdown, really helped! Wish I could pick you as an answer as well.
Sam
The __hash__() method can just be implemented as "return (x,y).__hash__()".
Peter Milley
+1  A: 

Any reason not to just use a tuple:

>>> s={}
>>> s[1,2]=5
>>> s
{(1, 2): 5}
>>> s[1,2]
5
Mark Tolonen
In that example I guess I could, but in my code the Point class has a few more properties so a tuple wouldn't be sufficient.
Sam
Check out the namedtuple function in the collections module. I find it very useful for implementing struct-like objects, and it creates classes you can inherit from if you want to add additional methods.
Peter Milley