inheritance

Alex Martelli aleax at aleax.it
Thu Jun 12 13:09:12 EDT 2003


Duncan Smith wrote:

> I'm in the process of subclassing some functionality of a class that was
> getting a bit too big.  But a lot of the inherited functions (eg.
> __mul__())
> return objects of the parent type.  Initially I had the following code...
> 
> 
> class Table(table.NumTable):
>     def __init__(self, values, variables, id = None):
>         table.NumTable.__init__(self, values, variables, id)
>         self.isBarnardised = 0
>         self.isRandRounded = 0
>         self.isConvRounded = 0
> 
> 
>     def __mul__(self, other):
>         res = table.NumTable.__mul__(self, other)
> 
>         return Table(res.values, res.variables)
> 
> 
> But I have ended up with this ...
> 
> 
> class Table(table.NumTable):
>     isBarnardised = 0
>     isRandRounded = 0
>     isConvRounded = 0
> 
> 
>     def __mul__(self, other):
>         res = table.NumTable.__mul__(self, other)
> 
>         return Table(res.values, res.variables)
> 
> 
> The questions I have are:
> 
> Is there any practical difference between the two?

If your extra attributes are all immutables, no; but if you use
this style extensively you'll eventually end up wanting to add
a list or dict attribute and the equivalence will break.

I would rather suggest that what you need is a factory that is
able to take a table.NumTable instance, wrap it up into the
corresponding Table instance, and return the latter.  E.g. in
the body of class Table you might have:

    def fromBaseInstance(cls, baseInstance):
        return cls(baseInstance.values, baseInstance.variables)
    fromBaseInstance = classmethod(fromBaseInstance)

then your implementation of e,g, __mul__ might be:

    def __mul__(self, other):
        res = table.NumTable.__mul__(self, other)
        return self.fromBaseInstance(res)

> Is there any alternative to explicitly overloading each of the parent
> class functions that return a new instance?

Once you have a systematic way to transform the baseclass methods'
results into the ones you want, it's quite feasible to avoid writing
out the boilerplate code, automating the process.  For example you
might have something like (warning, untested code):


class Table(table.NumTable):

    def __init__(self, values, variables, id = None):
        table.NumTable.__init__(self, values, variables, id)
        self.isBarnardised = 0
        self.isRandRounded = 0
        self.isConvRounded = 0

    def fromBaseInstance(cls, baseInstance):
        return cls(baseInstance.values, baseInstance.variables)
    fromBaseInstance = classmethod(fromBaseInstance)

    __wrap_ordinary_ = 'foo bar baz'.split()
    __wrap_special_ = 'mul add div'.split()

def wrapone(name):
    transformer = Table.fromBaseInstance
    wrappee = getattr(Table, name)
    def wrapper(self, *args):
        return transformer(wrappee(self, args))
    setattr(Table, name, wrappee)

for name in Table.__wrap_ordinary__:
    wrapone(name)
for name in Table.__wrap_special__:
    wrapone('__%s__' % name)


Alex





More information about the Python-list mailing list