automatically assigning names to indexes
François Pinard
pinard at iro.umontreal.ca
Tue Jul 12 09:38:10 EDT 2005
[simonwittber at gmail.com]
> 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)
--
François Pinard http://pinard.progiciels-bpi.ca
More information about the Python-list
mailing list