Python Vector Class

I come from C # background where this stuff is very simple - trying to translate to Python for Maya.

There must be a better way to do this. Basically, I'm looking to create a Vector class that will just have the 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, somehow.

This is what I have so far, but there should be a better way to do this than using the 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)) 
+6
python object class vector maya
source share
8 answers

Edit: I changed the code with my answer a little more from the original @unutbu to simplify it and make what is made more clear. In the latest version, @staticmethod completely excluded and replaced with nested single-line. The external function and nested class were renamed AutoFloatProperties and _AutoFloatProperties to reflect their specialized behavior in converting and storing values ​​assigned as float. 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(vx) # 1.0 vx=4 print(vx) # 4.0 
+4
source share

If I understand your question correctly, do you need 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 input values ​​and exposes them using property (using getters, setters); I abbreviated "getters" using a lambda expression.

Note that in Python it would be very common to manage these values ​​(say: x, y, z) directly on the object itself (I think you want to provide an explicit cast of a float?)

+5
source share

Perhaps I misunderstand your question, but I think that what you want is already done for you in collections.namedtuple :

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

Does this look right?

For shame, I forgot that tuples are unchanged. Curse me not to upgrade from Python 2.5, so I could test the code I wrote. In any case, you might need something similar to collections.namedtuple , with the exception of the rather hypothetical namedlist . Or you can completely abandon this idea and use something else. The fact is that this answer was incorrect, and I would delete it, except that I consider myself obligated to the people who supported me in order to correct my mistake.

+3
source share

Is this what you are looking for?

 class vector(object): def __init__(self, x,y,z): self.x = x self.y = y self.z = z # overload [] def __getitem__(self, index): data = [self.x,self.y,self.z] return data[index] # overload set [] def __setitem__(self, key, item): if (key == 0): self.x = item elif (key == 1): self.y = item elif (key == 2): self.z = item #TODO: Default should throw excetion 

This is the most naive way to do this. I am sure that some python guru will come with a smirk in my code and replace it with one liner.

Examples of this code:

 v = vector(1,2,3) v[1] = 4 v[2] = 5 vx = 1 vz= 66 
+2
source share

Edit: My previous answer tried to create a generic metaclass "AutoProperties", which I hope can be useful. As @martineau's answer shows, a solution specializing in the Vector class can simplify the task.

Here is another idea on these lines (specialized simplicity over generalized complexity). It uses a class decorator (which, I think, is a little easier to understand than a metaclass) and the idea of ​​@martineau to simplify getters and setters with default values:

 def AutoProperties(*props): def _AutoProperties(cls): for attr in props: def getter(self,_attr='_'+attr): return getattr(self, _attr) def setter(self, value, _attr='_'+attr): setattr(self, _attr, float(value)) setattr(cls,attr,property(getter,setter)) return cls return _AutoProperties @AutoProperties('x','y','z') 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 = map(float,(x, y, z)) 

Original answer: Here you can avoid repeating the boiler plate code when defining many similar properties.

I tried to make the solution quite general, so it can be useful to people in other situations next to this specific one.

To use it, you need to do 2 things:

  1. Placed
      __metaclass__=AutoProperties(('x','y','z')) 

    at the beginning of your class definition. You can list (as strings) as many attributes (e.g. x , y , z ) as you wish. AutoProperties will turn them into properties.

  2. Your class, for example. Vector , you need to define staticmethods _auto_setter and _auto_getter . They take one argument, the attribute name as a string, and return the setter or getter function, respectively, for that attribute.

The idea of ​​using metaclasses to automatically set properties comes from Guido Rossum's essay on properties and metaclasses . There, it defines an autoprop , similar to what I use below. The main difference is that AutoProperties expects the user to define recipient factories and setters instead of manual determinants and setters.

 def AutoProperties(props): class _AutoProperties(type): # Inspired by autoprop (http://www.python.org/download/releases/2.2.3/descrintro/) def __init__(cls, name, bases, cdict): super(_AutoProperties, cls).__init__(name, bases, cdict) for attr in props: fget=cls._auto_getter(attr) fset=cls._auto_setter(attr) setattr(cls,attr,property(fget,fset)) return _AutoProperties class Vector(object): '''Creates a Maya vector/triple, having x, y and z coordinates as float values''' __metaclass__=AutoProperties(('x','y','z')) def __init__(self, x=0, y=0, z=0): # I assume you want the initial values to be converted to floats too. self._x, self._y, self._z = map(float,(x, y, z)) @staticmethod def _auto_setter(attr): def set_float(self, value): setattr(self, '_'+attr, float(value)) return set_float @staticmethod def _auto_getter(attr): def get_float(self): return getattr(self, '_'+attr) return get_float if __name__=='__main__': v=Vector(1,2,3) print(vx) # 1.0 vx=4 print(vx) # 4.0 
+2
source share

I do not quite understand the question. You have a Vector that describes a point in space with 3 coordinates. Your implementation already allows you to change the values:

 v = Vector() vx = 10 # now x is 10 

Why would he return a tuple? What are you using it for? However, the tuple is immutable, so it cannot be changed, but you can use a list. Changing the numbers in this list will not be reflected in the document.

If you need to make sure the type is a float, consider property qualifiers :

 class C(object): def __init__(self): self._x = None @property def x(self): """I'm the 'x' property.""" return self._x @x.setter def x(self, value): print "x set to ", value self._x = value c = C() cx = 10 print cx, c._x 
+1
source share

You can easily imagine your vector:

 def __repr__(self): return "(%.1f, %.1f, %.1f)" % (self.x, self.y, self.z) 

When you execute methods with __ ... __ , like @Override in Java.

0
source share

I understand that

  • you want to have a filter that converts input values ​​to floats.
  • you do not want to write a property three times

You can use the following code:

 class Vector(object): def __init__(self, x,y,z): self._x = x def mangle(name): return '_'+name for name in ['x','y','z']: def set_xyz(self, value): self.__setattr__(mangle(name), float(value)) def get_xyz(self): return self.__getattribute__(mangle(name)) prop = property(get_xyz, set_xyz) setattr(Vector,name, prop) 
-one
source share

All Articles