Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python Vector Class

I'm coming from a C# background where this stuff is super easy—trying to translate into Python for Maya.

There's gotta' be a better way to do this. Basically, I'm looking to create a Vector class that will simply have x, y and z coordinates, but it would be ideal if this class returned a tuple with all 3 coordinates and if you could edit the values of this tuple through x, y and z properties, somehow.

This is what I have so far, but there must be a better way to do this than using an exec statement, right? I hate using exec statements.

class Vector(object):
    '''Creates a Maya vector/triple, having x, y and z coordinates as float values'''

    def __init__(self, x=0, y=0, z=0):
        self.x, self.y, self.z = x, y, z

    def attrsetter(attr):
        def set_float(self, value):
            setattr(self, attr, float(value))
        return set_float

    for xyz in 'xyz':
        exec("%s = property(fget=attrgetter('_%s'), fset=attrsetter('_%s'))" % (xyz, xyz, xyz))
like image 231
jedmao Avatar asked Mar 08 '10 12:03

jedmao


3 Answers

If I understand your question correctly, you want something like this ?

class Vector(object):

    def __init__(self, x=0, y=0, z=0):
        self._x, self._y, self._z = x, y, z

    def setx(self, x): self._x = float(x)
    def sety(self, y): self._y = float(y)        
    def setz(self, z): self._z = float(z)     

    x = property(lambda self: float(self._x), setx)
    y = property(lambda self: float(self._y), sety)
    z = property(lambda self: float(self._z), setz)

This uses _x, _y and _z to (internally) store the incoming values and exposes them via the use of property (with getters, setters); I abbreviated the 'getters' using a lambda statement.

Note that in Python it would be very common to manipulate these values (say: x, y, z) on the object itself directly (I guess you want ensure the explicit float casts?)

like image 175
ChristopheD Avatar answered Oct 04 '22 03:10

ChristopheD


Edit: I've modified the code with my answer a bit more from @unutbu's original to simplify it and make what is being done clearer. In the latest version, the @staticmethod's have been eliminated altogether and replaced with nested one-liners. The outer function and nested class have been renamed AutoFloatProperties and _AutoFloatProperties to reflect their specialized behavior of converting and storing the values assigned as floats. Despite all this, @unutbu's own revised answer using a class decorator instead of a metaclass is a slightly simpler solution, although the internals and usage are very similar.

def AutoFloatProperties(*props):
    '''metaclass'''
    class _AutoFloatProperties(type):
        # Inspired by autoprop (http://www.python.org/download/releases/2.2.3/descrintro/#metaclass_examples)
        def __init__(cls, name, bases, cdict):
            super(_AutoFloatProperties, cls).__init__(name, bases, cdict)
            for attr in props:
                def fget(self, _attr='_'+attr): return getattr(self, _attr)
                def fset(self, value, _attr='_'+attr): setattr(self, _attr, float(value))
                setattr(cls, attr, property(fget, fset))
    return _AutoFloatProperties

class Vector(object):
    '''Creates a Maya vector/triple, having x, y and z coordinates as float values'''
    __metaclass__ = AutoFloatProperties('x','y','z')
    def __init__(self, x=0, y=0, z=0):
        self.x, self.y, self.z = x, y, z # values converted to float via properties

if __name__=='__main__':
    v=Vector(1,2,3)
    print(v.x)
    # 1.0
    v.x=4
    print(v.x)
    # 4.0
like image 26
martineau Avatar answered Oct 04 '22 01:10

martineau


I may be misreading your question, but I think what you want is already made for you in collections.namedtuple:

>>> from collections import namedtuple
>>> Vector = namedtuple('Vector', 'x y z')
>>> v = Vector(0, 0, 0)
>>> v
Vector(x=0, y=0, z=0)
>>> v.x = 10
>>> v
Vector(x=10, y=0, z=0)
>>> tuple(v)
(10, 0, 0)
>>> v._asdict()
{'x': 10, 'y': 0, 'z': 0}
>>>

Does that look about right?

For shame, I forgot that tuples are immutable. Curse me for not upgrading from Python 2.5 so I could have actually tested the code I wrote. Anyway, you may want something quite similar to collections.namedtuple, except more like a hypothetical namedlist. Or you may want to discard that idea entirely and use something different. The point is that this answer was wrong, and I would delete it, except I feel obligated to the people who upvoted me to correct my mistake.

like image 29
Chris Lutz Avatar answered Oct 04 '22 01:10

Chris Lutz