Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Properly Implementing Python Star Operator for a Custom Class

Tags:

python

I have a Python class called Point, that is basically a holder for an x and y value with added functionality for finding distance, angle, and such with another Point.

For passing a point to some other function that may require the x and y to be separate, I would like to be able to use the * operator to unpack my Point to just the separate x, y values.

I have found that this is possible if I override __getitem__ and raise a StopIterationException for any index beyond 1, with x corresponding to 0 and y to 1.

However it doesn't seem proper to raise a StopIteration when a ValueError/KeyError would be more appropriate for values beyond 1.

Does anyone know of the correct way to implement the * operator for a custom class? Preferably, a way that does not raise StopIteration through __getitem__?

like image 661
Lucas Stertz Avatar asked Oct 10 '14 07:10

Lucas Stertz


2 Answers

You can implement the same by overriding the __iter__ magic method, like this

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

    def __iter__(self):
        return (self.__dict__[item] for item in sorted(self.__dict__))

def printer(x, y):
    print x, y

printer(*Point(2, 3))

Output

2 3
like image 164
thefourtheye Avatar answered Oct 02 '22 00:10

thefourtheye


Here's another way to do it that uses __dict__ but gives you precise control over the order without having to perform a sort on the keys for every access:

def __iter__(self): return (self.__dict__[item] for item in 'xy')

Of course, you could stash a sorted tuple, list or string of keys somewhere, but I think using a literal makes sense here.

And while I'm at it, here's one way to do the setter & getter methods.

def __getitem__(self, key): return (self.x, self.y)[key]

def __setitem__(self, key, val): setattr(self, 'xy'[key], val)
like image 36
PM 2Ring Avatar answered Oct 02 '22 00:10

PM 2Ring