Question about subclassing - version 2

F

Frank Millman

Hi all

I recently posted a question about subclassing. I did not explain my
full requirement very clearly, and my proposed solution was not pretty.
I will attempt to explain what I am trying to do more fully, and
describe a possible solution. It is still not pretty, so I would
appreciate any comments.

I have a base class (ClassA), which is an abstract class. Most of the
methods and attributes are common to all subclasses, so there is not
much they have to override.

I have a subclass (ClassB) of my base class, which is also abstract. It
represents a subset of ClassA, and overrides some of its methods. When
I create a concrete class (is that the correct term?) I subclass either
from ClassA or from ClassB.

Now I want to represent a different subset of ClassA, which overrides
some of its methods. This subset can apply to ClassB as well as to
ClassA.

In pseudo terms, I want ClassA1, ClassA2, ClassB1, and ClassB2 where A1
is the base class, B overides some methods, and 2 overrides other
methods, and I want to subclass from any of them.

My original solution involved passing 1 or 2 as an argument, and
putting some code into __init__ which redefined certain methods if it
received a 2. This worked, but it meant that I could not then easily
redefine the method again in a concrete class.

My new idea is to use multiple inheritance. This is how it would work.

class ClassA(object):
def __init__(self):
pass
def test1(self):
print 'Base method 1'
def test2(self):
print 'Base method 2'

class ClassB(ClassA):
def __init__(self):
ClassA.__init__(self)
def test1(self):
print 'Overriding method 1'

class Class2(object):
def test2(self):
print 'Overriding method 2'

Now I can set up the following concrete classes -

class ClassA1(ClassA):
def __init__(self):
ClassA.__init__(self)

class ClassA2(Class2,ClassA):
def __init__(self):
ClassA.__init__(self)

class ClassB1(ClassB):
def __init__(self):
ClassB.__init__(self)

class ClassB2(Class2,ClassB):
def __init__(self):
ClassB.__init__(self)

Now if I do the following, I get the results shown, which is what I
want -

ClassA1().test1() - 'Base method 1'
ClassA1().test2() - 'Base method 2'
ClassB1().test1() - 'Overriding method 1'
ClassB1().test2() - 'Base method 2'
ClassA2().test1() - 'Base method 1'
ClassA2().test2() - 'Overriding method 2'
ClassB2().test1() - 'Overriding method 1'
ClassB2().test2() - 'Overriding method 2'

Now for the real test -

class ClassC3(Class2,ClassB):
def __init__(self):
ClassB.__init__(self)
def test1(self):
print 'Overriding method 1 from ClassC3'
def test2(self):
print 'Overriding method 2 from ClassC3'

ClassC3().test1() - 'Overriding method 1 from ClassC3'
ClassC3().test2() - 'Overriding method 2 from ClassC3'

So it works. However, using multiple inheritance is not ideal, and I
believe it is not even supported in some languages. Can anyone suggest
a better way of tackling this problem?

Thanks

Frank Millman
 
B

bearophileHUGS

Frank Millman, just a short note, more expert people can give you
better answers. There aren't abstract classes in Python. They are all
concrete. You may have classes with undefined methods (they may raise
NotImplementedError).
Multiple inheritance isn't supported by Java and Ruby, but it is
supported by C++ and Python, so you can use it in Python.
There are also ways of mixing methods. You may define methods, and then
lists of methods to add to your classes.

Bye,
bearophile
 
F

Frank Millman

Frank Millman, just a short note, more expert people can give you
better answers. There aren't abstract classes in Python. They are all
concrete. You may have classes with undefined methods (they may raise
NotImplementedError).
Multiple inheritance isn't supported by Java and Ruby, but it is
supported by C++ and Python, so you can use it in Python.
There are also ways of mixing methods. You may define methods, and then
lists of methods to add to your classes.

Bye,
bearophile

I use the term 'abstract class' in the abstract sense :)

Say I have three classes where 90% of the attributes and methods are
common. It makes sense to create a base class with these attributes and
methods, and turn each of the three classes into a subclass which
inherits from the base class and overrides the bits that are unique to
each one.

This is what I call an abstract class. Maybe there is a more correct
term.

Frank
 
B

Bruno Desthuilliers

Frank Millman, just a short note, more expert people can give you
better answers. There aren't abstract classes in Python.

Well... There's no "abstract" modifier at least - but there still are
abstract classes, ie not meant to be directly instanciated. You
mentioned NotImplementedError, which is indeed the usual way to make
something "abstract" in Python.
 
B

Bruno Desthuilliers

Frank said:
Hi all

I recently posted a question about subclassing. I did not explain my
full requirement very clearly, and my proposed solution was not pretty.
I will attempt to explain what I am trying to do more fully, and
describe a possible solution. It is still not pretty, so I would
appreciate any comments.

I have a base class (ClassA), which is an abstract class. Most of the
methods and attributes are common to all subclasses, so there is not
much they have to override.

I have a subclass (ClassB) of my base class, which is also abstract. It
represents a subset of ClassA, and overrides some of its methods. When
I create a concrete class (is that the correct term?) I subclass either
from ClassA or from ClassB.

Now I want to represent a different subset of ClassA, which overrides
some of its methods. This subset can apply to ClassB as well as to
ClassA.

In pseudo terms, I want ClassA1, ClassA2, ClassB1, and ClassB2 where A1
is the base class, B overides some methods, and 2 overrides other
methods, and I want to subclass from any of them.

My original solution involved passing 1 or 2 as an argument, and
putting some code into __init__ which redefined certain methods if it
received a 2. This worked, but it meant that I could not then easily
redefine the method again in a concrete class.

My new idea is to use multiple inheritance. This is how it would work.

class ClassA(object):
def __init__(self):
pass
def test1(self):
print 'Base method 1'
def test2(self):
print 'Base method 2'

class ClassB(ClassA):
def __init__(self):
ClassA.__init__(self)
def test1(self):
print 'Overriding method 1'

class Class2(object):
def test2(self):
print 'Overriding method 2'

To be pedantic, Class2.test2 is not overridding anything, since there's
no "test2" method in it's parent class.
Now I can set up the following concrete classes -

class ClassA1(ClassA):
def __init__(self):
ClassA.__init__(self)

If that's the only thing you do in the __init__, then don't bother write
an init method at all.
class ClassA2(Class2,ClassA):
def __init__(self):
ClassA.__init__(self)

May I suggest having a look at super() ?
class ClassB1(ClassB):
def __init__(self):
ClassB.__init__(self)

class ClassB2(Class2,ClassB):
def __init__(self):
ClassB.__init__(self)

Now if I do the following, I get the results shown, which is what I
want -

ClassA1().test1() - 'Base method 1'
ClassA1().test2() - 'Base method 2'
ClassB1().test1() - 'Overriding method 1'
ClassB1().test2() - 'Base method 2'
ClassA2().test1() - 'Base method 1'
ClassA2().test2() - 'Overriding method 2'
ClassB2().test1() - 'Overriding method 1'
ClassB2().test2() - 'Overriding method 2'

Now for the real test -

class ClassC3(Class2,ClassB):
def __init__(self):
ClassB.__init__(self)
def test1(self):
print 'Overriding method 1 from ClassC3'
def test2(self):
print 'Overriding method 2 from ClassC3'

ClassC3().test1() - 'Overriding method 1 from ClassC3'
ClassC3().test2() - 'Overriding method 2 from ClassC3'

So it works. However, using multiple inheritance is not ideal,

Why so ? Multiple inheritence is a pretty useful tool - but it can
become tricky very soon. IMHO, it's best use is for mixin classes...
and I
believe it is not even supported in some languages.

A lot of things aren't even supported in some languages !-)
Can anyone suggest
a better way of tackling this problem?

Not out of my hat. Just a few considerations on Python and OO: Python
being dynamically typed, inheritence is only about sharing
implementation. There's another way to do share implementation -
composition/delegation. It's more flexible, and can avoid "cartesian
product" multiplication of classes. It's also less advertised than
inheritance - probably because of "some languages" that fail to offer
any support for it. The good news here is that Python makes it a breeze,
thanks to the __getattr__/__setattr__ hooks. Now I don't know if it
makes any sense WRT/ your current problem...
 
B

Bruno Desthuilliers

Frank said:
(snip)
I use the term 'abstract class' in the abstract sense :)

Say I have three classes where 90% of the attributes and methods are
common. It makes sense to create a base class with these attributes and
methods, and turn each of the three classes into a subclass which
inherits from the base class and overrides the bits that are unique to
each one.

This is what I call an abstract class. Maybe there is a more correct
term.

Depends if instanciating this base class would make any sense.
 
F

Frank Millman

Bruno said:
Depends if instanciating this base class would make any sense.

It would not make sense, no.

I have not gone to the trouble of raising NotImplementedError - the
methods that the subclasses *must* override just have a 'pass'
statement. I guess it would be more correct to raise the error, as it
would give me a quicker indication of an error if I happened to omit
one, but in practice I would find out pretty quickly anyway.

Frank
 
M

Maric Michaud

Le vendredi 08 septembre 2006 09:51, (e-mail address removed) a écrit :
Frank Millman, just a short note, more expert people can give you
better answers. There aren't abstract classes in Python. They are all
concrete.
Really ? This is like saying there is no singleton in Python...

class AbstractClass(object) :

def __init__(self) : raise RuntimeError('Ths class is an abstract one !')

The abstract class can then define APIs (methods which raise
NotImplementedError) and/or logic (fully implemented methods).
With this scheme you are not stuck to the "API only" usage of abstract classes
like in Java nad its interfaces.
You may have classes with undefined methods (they may raise
NotImplementedError).

C++ "pure virtual methods" is only the C++ way of doing something which is a
more general concept in OOP.


--
_____________

Maric Michaud
_____________

Aristote - www.aristote.info
3 place des tapis
69004 Lyon
Tel: +33 426 880 097
 
M

Maric Michaud

Le vendredi 08 septembre 2006 10:15, Bruno Desthuilliers a écrit :
You
mentioned NotImplementedError, which is indeed the usual way to make
something "abstract" in Python.

Hummm, some more thoughts about this.

I can imagine class hierarchies where the presence of not implemented methods
doesn't mean that the class is actually an abstract one. Even if partial
implementation is not a common scheme it can save you from writing a lot of
classes.

For example :

class car : #abstract
def accelerate() : raise NotimplementedError
def getBaseBuildPrice() : raise NotimplementedError
def getOptionsPrice() : raise NotimplementedError
def getPublicPrice() : raise NotimplementedError

class SerieA(car) :
"""abstract, it's before the car
get out of the factory"""
def accelerate() : ...
def getBaseBuildPrice() : ...

class CrashTestVendorSerieA(SerieA) :
"""concrete, but doesn't
implement getPublicPrice"""
def getOptionsPrice() : ...

class CommercialSerieA(SerieA) :
def getOptionsPrice() : ...
def getPublicPrice() : ....

Doing the same with more traditional object design give :

class car : #abstract
def accelerate() : raise NotimplementedError
def getBaseBuildPrice() : raise NotimplementedError

class SerialCar : #abstract
def getOptionsPrice() : raise NotimplementedError

class ComercialCar : #abstract
def getPublicPrice() : raise NotimplementedError

class SerieA(car, SeriialCar) : #abstract
def accelerate() : ...
def getBaseBuildPrice() : ...

class CrashTestVendorSerieA(SerieA) : # concrete
def getOptionsPrice() : ...

class CommercialSerieA(SerieA, CommercialCar) : # concrete
def getOptionsPrice() : ...
def getPublicPrice() : ...

And this can become a true spider net for more complicated cases. Obviously,
in practice we will choose alternatives to inheritance (strategies,
visitors, ...) to work with such complex situations, but it seems to me that
partial implementation is not a bad choice, specifically in regard to duck
typing.


--
_____________

Maric Michaud
_____________

Aristote - www.aristote.info
3 place des tapis
69004 Lyon
Tel: +33 426 880 097
 
B

Bruno Desthuilliers

Frank said:
It would not make sense, no.

I have not gone to the trouble of raising NotImplementedError - the
methods that the subclasses *must* override just have a 'pass'
statement. I guess it would be more correct to raise the error, as it
would give me a quicker indication of an error if I happened to omit
one, but in practice I would find out pretty quickly anyway.

Mmm... My own experience is that methods that *must* be redefined are
better raising NotImplementedError. Makes things more obvious IMHO.

Now there are of course methods that are only provided as hooks - here
it's ok to have some no-op default behaviour.

My 2 cents
 
F

Frank Millman

Bruno said:
Mmm... My own experience is that methods that *must* be redefined are
better raising NotImplementedError. Makes things more obvious IMHO.

Can't argue with that. I have just gone through my app and changed them
all :)

Frank
 
B

Bruno Desthuilliers

Maric Michaud a écrit :
Le vendredi 08 septembre 2006 10:15, Bruno Desthuilliers a écrit :



Hummm, some more thoughts about this.

I can imagine class hierarchies where the presence of not implemented methods
doesn't mean that the class is actually an abstract one.

I can imagine this too - as well as I can imagine the nightmarish
spaghetti code that could use this class hierarchie.
Even if partial
implementation is not a common scheme it can save you from writing a lot of
classes.

A partial implementation should return default values, not raise (aka
NullObjectPattern). Well, IMHO at least.

(snip)
And this can become a true spider net for more complicated cases. Obviously,
in practice we will choose alternatives to inheritance (strategies,
visitors, ...)
Composition/delegation...

to work with such complex situations,

Indeed.
 

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,995
Messages
2,570,236
Members
46,821
Latest member
AleidaSchi

Latest Threads

Top