Inheritance Question

Carl Banks pavlovevidence at gmail.com
Sat Nov 11 12:02:23 CET 2006


Jackson wrote:
> For a more "concrete" example:
>
> Suppose all the animals in the world have only 1 or 2 legs.
>
> class Legs(object)
>   def run(): pass
>   def walk(number_of_legs):
>     # lots of commands
>     # that do not depend on the
>     # number of legs but definitely
>     # have to do with walking
>
>     if number_of_legs == '1':
>        # blah blah
>
>     if number_of_legs == '2':
>        # blah blah
>
>     # more commands
>
> class HasAtLeastOneLeg(Legs):
>   def walk():
>     # Legs.walk(number_of_legs=1)
>
> class HasTwoLegs(HasAtLeastOneLeg,Legs):
>   def walk()
>     # Legs.walk(number_of_legs=2)

Well, I would have done this differently.  If the only difference in
behavior between one- and two-legged creatures is their gait, I
probably wouldn't have bothered breaking Legs into subclasses.  I'd
just pass the number of legs into __init__:

class Legs(object):
    def __init__(self,number_of_legs):
        self.number_of_legs = number_of_legs
    def walk(self):
        # non-legs-dependent stuff
        if self.number_of_legs == 1:
            # one-legged gait
        elif self.number_of_legs == 2:
            # two-legged gait
        else:
            raise ValueError("invalid number of legs")
        # more non-legs-dependent stuff

Then, when defining some sort of monster class, I'd do something like
this:

class Human(object):
    def __init__(self):
        self.legs = Legs(2)
        # etc.

class Dufflepud(object):
    def __init__(self):
        self.legs = Legs(1)
        # etc.

If there's more to it than just gait (say for example, one-legged
creatures attack differently, jump higher, and can't turn invisible
because they don't have enough limbs to cast Level 4 Invisibility),
then I would factor out differences in behavior into subclasses.

class Legs(object):
    def walk(self):
        # non-leg-related stuff
        self.set_gait()
        # more non-leg-related stuff

class OneLeg(Legs):
    def set_gait(self):
        # set the one-legged gait

class TwoLegs(Legs):
    def set_gait(self):
        # set the two-legged gait

class Human(object):
    def __init__(self):
        self.legs = TwoLegs()

class Dufflepud(object):
    def __init__(self):
        self.legs = OneLeg()


ISTM you have been missing out on the true power of inheritance.
Behavior exclusive to creatures with two legs should be implmented in
the TwoLegs class, but in your example you define a HasTwoLegs class
yet still implement this behavior in the base class Legs.  That's just
poor use of inheritance.  My examples implement behavior exclusive to
two-legged creatures inside the TwoLegs class, where it belongs.

In fact, BEHAVIOR is the key to arranging class hierarchies.  (The
classical advice, the inheritance represents the "is a" relationship,
can be very misleading and I don't recommend using it.  The focus
should be on behavior.)  Anyways, in your universe, there are three
kinds of behaviors:

A. behaviors exclusive to one-footed creatures
B. behaviors exclusive to two-footed creatures
C. behaviors common to both

Once you consider behaviors in this way, how to arrange the class
hierarchy becomes obvious.  Each kind of behavior shoud be implemented
in its own class: common behaviors go into base classes, exclusive
behaviors into subclasses.  The hierarchy should look like this:

class Legs -- this should implement all common behavior
class OneLeg(Legs) -- should implment all behavior exclusive to
one-legged creatures
class TwoLegs(Legs) -- should implment all behavior exclusive to
two-legged creatures

Note that there's no need for an AtLeastOneLeg class.  There are only
one- and two-legged creatures in this univserse, so behaviors exclusive
to creatures with at least one leg apply are in fact common to all
creatures.  But suppose we add zero-footed creatures to the universe.
Now we identify five kinds of behaviors:

A. behaviors common to all creatures
B. behaviors exclusive to zero-footed creatures
C. behaviors common to one- and two-footed creatures
D. behaviors exclusive to one-footed creatures
E. behaviors exclusive to two-footed creatures

Now we need an AtLeastOneLeg class in our hierarchy:

class Legs
class ZeroLegs(Legs)
class AtLeastOneLeg(Legs)
class OneLeg(AtLeastOneLeg)
class TwoLegs(AtLeastOneLeg)

But, notice that we still have a OneLeg class, because there's still
behavior exclusive to one-legged creatures.  It belongs in OneLeg, not
in AtLeastOneLeg.

Hope this long-winded advice helps.


Carl Banks




More information about the Python-list mailing list