automatically assigning names to indexes

S

simonwittber

I know its been done before, but I'm hacking away on a simple Vector
class.

class Vector(tuple):
def __add__(self, b):
return Vector([x+y for x,y in zip(self, b)])
def __sub__(self, b):
return Vector([x-y for x,y in zip(self, b)])
def __div__(self, b):
return Vector([x/b for x in self])
def __mul__(self, b):
return Vector([x*b for x in self])

I like it, because it is simple, and can work with vectors of any
size...

However, I'd like to add attribute access (magically), so I can do
this:

v = Vector((1,2,3))
print v.x
print v.y
print v.z

as well as:

print v[0]
print v[1]
print v[2]

Has anyone got any ideas on how this might be done?


Sw.
 
P

Peter Otten

However, I'd like to add attribute access (magically), so I can do
this:

v = Vector((1,2,3))
print v.x
print v.y
print v.z

as well as:

print v[0]
print v[1]
print v[2]

Has anyone got any ideas on how this might be done?
.... x = property(lambda self: self[0])
.... y = property(lambda self: self[1])
.... z = property(lambda self: self[2])
....
Vector("abc") ('a', 'b', 'c')
Vector("abc").z 'c'
Vector("abc")[2]
'c'

However, no magic is involved.

Peter
 
S

simonwittber

class Vector(tuple):
... x = property(lambda self: self[0])
... y = property(lambda self: self[1])
... z = property(lambda self: self[2])
...
Vector("abc") ('a', 'b', 'c')
Vector("abc").z 'c'
Vector("abc")[2]
'c'

Aha! You have simultaneously proposed a neat solution, and shown me a
bug in my class! (It shouldn't accept strings)

Thanks.

Sw.
 
G

George Sakkis

I know its been done before, but I'm hacking away on a simple Vector
class.

class Vector(tuple):
def __add__(self, b):
return Vector([x+y for x,y in zip(self, b)])
def __sub__(self, b):
return Vector([x-y for x,y in zip(self, b)])
def __div__(self, b):
return Vector([x/b for x in self])
def __mul__(self, b):
return Vector([x*b for x in self])

I like it, because it is simple, and can work with vectors of any
size...

However, I'd like to add attribute access (magically), so I can do
this:

v = Vector((1,2,3))
print v.x
print v.y
print v.z

as well as:

print v[0]
print v[1]
print v[2]

Has anyone got any ideas on how this might be done?

And what should happen for vectors of size != 3 ? I don't think that a
general purpose vector class should allow it; a Vector3D subclass would
be more natural for this.

George
 
?

=?iso-8859-1?Q?Fran=E7ois?= Pinard

[[email protected]]
I know its been done before, but I'm hacking away on a simple Vector
class. [...] However, I'd like to add attribute access (magically),
so I can do this: [...] Has anyone got any ideas on how this might be
done?

I needed something this last week, while toying with rotations. Allow
me to humbly offer my solution, meant for more than one class. Yet for
brievety, I'm limiting my example to a single class. A few constructors
are used in the example, but the corresponding classes are missing.

I hesitated a bit between having my rotation objects be modifiable or
not, and finally opted for the later (consequently, the constructor is
neatly called for a resulting object). If you really want a modifiable
object, derive from `list' instead of deriving from `tuple', rename
`NamedTuple' into `NamedList', and wihin sub-classes, initialise your
object with `__init__(self, ...)' rather than with `__new__(cls, ...)'.



__metaclass__ = type
import math

# Almost zero, but not yet.
epsilon = 1e-9

from math import pi
half_pi = .5*pi

class NamedTuple(tuple):

class __metaclass__(type):

def __new__(cls, name, bases, definitions):
self = type.__new__(cls, name, bases, definitions)
if hasattr(self, '__names__'):
def make_property(index):
def getter(self): return self[index]
return property(getter)
for index, name in enumerate(self.__names__):
setattr(self, name, make_property(index))
return self

class Quaternion(NamedTuple):
__names__ = 'w', 'x', 'y', 'z'

def __new__(cls, w, x, y, z):
l = 1./math.sqrt(w*w + x*x + y*y + z*z)
return cls.new(w*l, x*l, y*l, z*l)

def new(cls, w, x, y, z):
if w < 0.:
self = tuple.__new__(cls, (-w, -x, -y, -z))
else:
self = tuple.__new__(cls, (w, x, y, z))
assert self.is_normal(), self
return self
new = classmethod(new)

def is_normal(self):
# For debugging only.
w, x, y, z = self
return abs(w*w + x*x + y*y + z*z - 1.) < epsilon and w >= 0.

def __eq__(self, other):
w1, x1, y1, z1 = self
w2, x2, y2, z2 = other
return abs(w1-w2) + abs(x1-x2) + abs(y1-y2) + abs(z1-z2) < epsilon

def __ne__(self, other):
return not self == other

def __mul__(self, other):
w1, x1, y1, z1 = self
w2, x2, y2, z2 = other
return Quaternion.new(w1*w2 - x1*x2 - y1*y2 - z1*z2,
w1*x2 + x1*w2 + y1*z2 - z1*y2,
w1*y2 + y1*w2 - x1*z2 + z1*x2,
w1*z2 + z1*w2 + x1*y2 - y1*x2)

def __div__(self, other):
w1, x1, y1, z1 = self
w2, x2, y2, z2 = other
return Quaternion.new( w1*w2 + x1*x2 + y1*y2 + z1*z2,
-w1*x2 + x1*w2 - y1*z2 + z1*y2,
-w1*y2 + y1*w2 + x1*z2 - z1*x2,
-w1*z2 + z1*w2 - x1*y2 + y1*x2)

def __rdiv__(self, other):
if not isinstance(other, (int, long, float)):
raise TypeError("unsupported operand type(s) for /")
w, x, y, z = self
return Quaternion.new(w, -x, -y, -z)

__truediv__ = __div__
__rtruediv__ = __rdiv__

def euler(self):
w, x, y, z = self
x2 = x + x
y2 = y + y
z2 = z + z
xx2 = x2*x
yy2 = y2*y
zz2 = z2*z
wx2 = x2*w
wy2 = y2*w
wz2 = z2*w
xy2 = x2*y
yz2 = y2*z
zx2 = z2*x
siny = wy2 - zx2
if abs(abs(siny) - 1) > epsilon:
return Euler.new(math.asin(siny),
math.atan2(yz2 + wx2, 1. - xx2 - yy2),
math.atan2(xy2 + wz2, 1. - yy2 - zz2))
if siny > 0.:
y = half_pi
else:
y = -half_pi
return Euler.new(math.atan2(-(yz2 - wx2), 1. - xx2 - zz2), y, 0.)

def matrix(self):
w, x, y, z = self
x2 = x + x
y2 = y + y
z2 = z + z
xx2 = x2*x
yy2 = y2*y
zz2 = z2*z
wx2 = x2*w
wy2 = y2*w
wz2 = z2*w
xy2 = x2*y
yz2 = y2*z
zx2 = z2*x
return Matrix(1. - yy2 - zz2, xy2 + wz2, zx2 - wy2,
xy2 - wz2, 1. - xx2 - zz2, yz2 + wx2,
zx2 + wy2, yz2 - wx2, 1. - xx2 - yy2)
 
S

simonwittber

And what should happen for vectors of size != 3 ? I don't think that a
general purpose vector class should allow it; a Vector3D subclass would
be more natural for this.

That's the 'magic' good idea I'm looking for. I think a unified Vector
class for all size vectors is a worthy goal!
 
G

George Sakkis

That's the 'magic' good idea I'm looking for. I think a unified Vector
class for all size vectors is a worthy goal!

What 'magic' ? The (x,y,z) notation is used only for 3D vectors. (x,y)
is also common for 2D and perhaps (t,x,y,z) for 4D, with t for time.
There are only 26 letters (or 52 if you allow capitals), so I don't see
how this can be generalized _usefully_ to arbitrary number of
dimensions.

George
 
D

Devan L

import math
class Vector:
def __init__(self, coordinates):
self.coordinates = coordinates
self.magnitude = sum([c**2 for c in coordinates])**0.5
self.direction = getangle(Vector([1]+[0 for i in
range(len(coordinates)-1)]))
def dotproduct(self, vector):
sum([a*b for a,b in zip(self.coordinates,vector.coordinates)])
def crossproduct(self, vector, pvector):
return
pvector*self.magnitude*vector.magnitude*math.sin(self.getangle(vector))
def getangle(self, vector):
return
math.acos(self.dotproduct(vector)/(self.magnitude*vector.magnitude))
def __mul__(self, scalar):
return Vector([c*scalar for c in self.coordinates])
def __add__(self, vector):
return Vector([c+d for c,d in
zip(self.coordinates,vector.coordinates)])
def __sub__(self, vector):
return Vector([c-d for c,d in
zip(self.coordinates,vector.coordinates)])

What about this?
 
S

Skip Montanaro

George> What 'magic' ? The (x,y,z) notation is used only for 3D
George> vectors. (x,y) is also common for 2D and perhaps (t,x,y,z) for
George> 4D, with t for time.

Don't forget (w,x,y,z) for quaternions...

Skip
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,575
Members
45,053
Latest member
billing-software

Latest Threads

Top