Some of the classes in Qahirah, my Cairo binding 
<https://github.com/ldo/qahirah> I found handy to reuse elsewhere, for example 
in my binding for Pixman <https://github.com/ldo/python_pixman>. Subclassing is 
easy, but then you need to ensure that operations inherited from the superclass 
return instances of the right class. This means that superclass methods must 
never refer directly to the class by name for constructing new objects; they 
need to obtain the current class by more indirect means.

(“isinstance” checks on the superclass name are fine, since they will succeed 
on subclasses as well.)

For example, consider the qahirah.Matrix class. Here is how I define the 
multiplication operator:

    def __mul__(m1, m2) :
        "returns concatenation with another Matrix, or mapping of a Vector."
        if isinstance(m2, Matrix) :
            result = m1.__class__ \
              (
                xx = m1.xx * m2.xx + m1.xy * m2.yx,
                yx = m1.yx * m2.xx + m1.yy * m2.yx,
                xy = m1.xx * m2.xy + m1.xy * m2.yy,
                yy = m1.yx * m2.xy + m1.yy * m2.yy,
                x0 = m1.xx * m2.x0 + m1.xy * m2.y0 + m1.x0,
                y0 = m1.yx * m2.x0 + m1.yy * m2.y0 + m1.y0,
              )
        elif isinstance(m2, Vector) :
            result = m2.__class__ \
              (
                x = m2.x * m1.xx + m2.y * m1.xy + m1.x0,
                y = m2.x * m1.yx + m2.y * m1.yy + m1.y0
              )
        else :
            result = NotImplemented
        #end if
        return \
            result
    #end __mul__

The idea behind this is that you can do “matrix * matrix” to do matrix 
multiplication, or “matrix * vector” to transform a Vector by a Matrix.

In Pixman, the corresponding class names are Transform instead of Matrix, and 
Point instead of Vector (following the usual pixman terminology). But the 
inherited multiplication operation still works on them.

For another example, consider the qahirah.Vector.from_tuple method, which 
allows easy passing of a simple pair of (x, y) coordinates wherever a Vector is 
wanted, with only a little extra work:

    @classmethod
    def from_tuple(celf, v) :
        "converts a tuple of 2 numbers to a Vector. Can be used to ensure that" 
\
        " v is a Vector."
        if not isinstance(v, celf) :
            v = celf(*v)
        #end if
        return \
            v
    #end from_tuple

The key thing here is to avoid staticmethods and use classmethods instead, so 
you get passed the class object and can use it to construct new instances.
-- 
https://mail.python.org/mailman/listinfo/python-list

Reply via email to