[Numpy-discussion] subclassing matrix

Basilisk96 basilisk96 at gmail.com
Thu Jan 10 01:08:03 EST 2008


Hello folks,

In the course of a project that involved heavy use of geometry and
linear algebra, I found it useful to create a Vector subclass of
numpy.matrix (represented as a column vector in my case).

I'd like to hear comments about my use of this "class promotion"
statement in __new__:
    ret.__class__ = cls

It seems to me that it is hackish to just change an instance's class
on the fly, so perhaps someone could clue me in on a better practice.
Here is my reason for doing this:
Many applications of this code involve operations between instances of
numpy.matrix and instances of Vector, such as applying a linear-
operator matrix on a vector. If I omit that "class promotion"
statement, then the results of such operations cannot be instantiated
as Vector types:
    >>> from vector import Vector
    >>> import numpy
    >>> u = Vector('1 2 3')
    >>> A = numpy.matrix('2 0 0; 0 2 0; 0 0 2')
    >>> p = Vector(A * u)
    >>> p.__class__
    <class 'numpy.core.defmatrix.matrix'>

This is undesirable because the calculation result loses the custom
Vector methods and attributes that I want to use. However, if I use
that "class promotion" statement, the p.__class__ lookup returns what
I want:
    >>> p.__class__
    <class 'vector.Vector'>

Is there a better way to achieve that?

Here is the partial subclass code:
#---------- vector.py
import numpy as _N
import math as _M
#default tolerance for equality tests
TOL_EQ = 1e-6
#default format for pretty-printing Vector instances
FMT_VECTOR_DEFAULT = "%+.5f"

class Vector(_N.matrix):
    """
    2D/3D vector class that supports numpy matrix operations and more.

    Examples:
        u = Vector([1,2,3])
        v = Vector('3 4 5')
        w = Vector([1, 2])
    """
    def __new__(cls, data="0. 0. 0.", dtype=_N.float64):
        """
        Subclass instance constructor.

            If data is not specified, a zero Vector is constructed.
            The constructor always returns a Vector instance.
            The instance gets a customizable Format attribute, which
controls the printing precision.
        """
        ret = super(Vector, cls).__new__(cls, data, dtype=dtype)
        #promote the instance to cls type.
        ret.__class__ = cls
        assert ret.size in (2, 3), 'Vector must have either two or
three components'
        if ret.shape[0] == 1:
            ret = ret.T
        assert ret.shape == (ret.shape[0], 1), 'could not express
Vector as a Mx1 matrix'
        if ret.shape[0] == 2:
            ret = _N.vstack((ret, 0.))
        ret.Format = FMT_VECTOR_DEFAULT
        return ret

    def __str__(self):
        fmt = getattr(self, "Format", FMT_VECTOR_DEFAULT)
        fmt = ', '.join([fmt]*3)
        return ''.join(["(", fmt, ")"]) % (self.X, self.Y, self.Z)

    def __repr__(self):
        fmt = ', '.join(['%s']*3)
        return ''.join(["%s([", fmt, "])"]) %
(self.__class__.__name__, self.X, self.Y, self.Z)

    #### the remaining methods are Vector-specific math operations,
including the X,Y,Z properties...


Cheers,
-Basilisk96



More information about the NumPy-Discussion mailing list