FAQ
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

Search Discussions

  • BearophileHUGS at Sep 8, 2006 at 7:51 am
    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
  • Frank Millman at Sep 8, 2006 at 8:05 am

    bearophileHUGS at lycos.com wrote:
    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
  • Bruno Desthuilliers at Sep 8, 2006 at 8:15 am

    bearophileHUGS at lycos.com wrote:
    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.



    --
    bruno desthuilliers
    python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
    p in 'onurb at xiludom.gro'.split('@')])"
  • Bruno Desthuilliers at Sep 8, 2006 at 8:34 am

    Frank Millman wrote:
    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...


    --
    bruno desthuilliers
    python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
    p in 'onurb at xiludom.gro'.split('@')])"
  • Bruno Desthuilliers at Sep 8, 2006 at 9:38 am

    Frank Millman wrote:
    bearophileHUGS at lycos.com wrote:
    There aren't abstract classes in Python. They are all
    concrete.
    (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.

    --
    bruno desthuilliers
    python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
    p in 'onurb at xiludom.gro'.split('@')])"
  • Frank Millman at Sep 8, 2006 at 9:53 am

    Bruno Desthuilliers wrote:
    Frank Millman wrote:
    bearophileHUGS at lycos.com wrote:
    There aren't abstract classes in Python. They are all
    concrete.
    (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.
    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
  • Maric Michaud at Sep 8, 2006 at 10:12 am

    Le vendredi 08 septembre 2006 09:51, bearophileHUGS at lycos.com 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
  • Maric Michaud at Sep 8, 2006 at 11:15 am

    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
  • Bruno Desthuilliers at Sep 8, 2006 at 2:30 pm

    Frank Millman wrote:
    Bruno Desthuilliers wrote:
    Frank Millman wrote:
    bearophileHUGS at lycos.com wrote:
    There aren't abstract classes in Python. They are all
    concrete.
    (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.
    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
    Frank

    --
    bruno desthuilliers
    python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
    p in 'onurb at xiludom.gro'.split('@')])"
  • Frank Millman at Sep 8, 2006 at 2:55 pm

    Bruno Desthuilliers wrote:
    Frank Millman wrote:
    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.
    Can't argue with that. I have just gone through my app and changed them
    all :-)

    Frank
  • Steve Holden at Sep 8, 2006 at 3:53 pm

    Frank Millman wrote:
    Bruno Desthuilliers wrote:
    Frank Millman wrote:
    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.

    Can't argue with that. I have just gone through my app and changed them
    all :-)
    Yup, if a thing can't go wrong, it won't

    regards
    Steve
    --
    Steve Holden +44 150 684 7255 +1 800 494 3119
    Holden Web LLC/Ltd http://www.holdenweb.com
    Skype: holdenweb http://holdenweb.blogspot.com
    Recent Ramblings http://del.icio.us/steve.holden
  • Bruno Desthuilliers at Sep 9, 2006 at 6:24 pm

    Maric Michaud a ?crit :
    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.
    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.

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
grouppython-list @
categoriespython
postedSep 8, '06 at 7:03a
activeSep 9, '06 at 6:24p
posts13
users6
websitepython.org

People

Translate

site design / logo © 2022 Grokbase