views:

2308

answers:

11

I apologize if this question is long. This was part of a blog post I did some time ago, and a reader suggested me to post it on stackoverflow. I trimmed it a bit though.

Suppose you have the following situation

#include <iostream>

class Animal {
public:
    virtual void speak() = 0;
};

class Dog : public Animal {
    void speak() { std::cout << "woff!" <<std::endl; }
};

class Cat : public Animal {
    void speak() { std::cout << "meow!" <<std::endl; }
};

void makeSpeak(Animal &a) {
    a.speak();
}

int main() {
    Dog d;
    Cat c;
    makeSpeak(d);
    makeSpeak(c);
}

As you can see, makeSpeak is a routine that accepts a generic Animal object. In this case, Animal is quite similar to a Java interface, as it contains only a pure virtual method. makeSpeak does not know the nature of the Animal it gets passed. It just sends it the signal “speak” and leaves the late binding to take care of which method to call: either Cat::speak() or Dog::speak(). This means that, as far as makeSpeak is concerned, the knowledge of which subclass is actually passed is irrelevant.

But what about Python? Let’s see the code for the same case in Python. Please note that I try to be as similar as possible to the C++ case for a moment:

class Animal(object):
    def speak(self):
        raise NotImplementedError()

class Dog(Animal):
    def speak(self):
        print "woff!"

class Cat(Animal):
    def speak(self):
        print "meow"

def makeSpeak(a):
    a.speak()

d=Dog()
c=Cat()
makeSpeak(d)
makeSpeak(c)

Now, in this example you see the same strategy. You use inheritance to leverage the hierarchical concept of both Dogs and Cats being Animals. But in Python, there’s no need for this hierarchy. This works equally well

class Dog:
    def speak(self):
        print "woff!"

class Cat:
    def speak(self):
        print "meow"

def makeSpeak(a):
    a.speak()

d=Dog()
c=Cat()
makeSpeak(d)
makeSpeak(c)

In Python you can send the signal “speak” to any object you want. If the object is able to deal with it, it will be executed, otherwise it will raise an exception. Suppose you add a class Airplane to both codes, and submit an Airplane object to makeSpeak. In the C++ case, it won’t compile, as Airplane is not a derived class of Animal. In the Python case, it will raise an exception at runtime, which could even be an expected behavior.

On the other side, suppose you add a MouthOfTruth class with a method speak(). In the C++ case, either you will have to refactor your hierarchy, or you will have to define a different makeSpeak method to accept MouthOfTruth objects, or in java you could extract the behavior into a CanSpeakIface and implement the interface for each. There are many solutions...

What I’d like to point out is that I haven’t found a single reason yet to use inheritance in Python (apart of frameworks and trees of exceptions, but I guess that alternative strategies exist). you don’t need to implement a base-derived hierarchy to perform polymorphically. If you want to use inheritance to reuse implementation, you can accomplish the same through containment and delegation, with the added benefit that you can alter it at runtime, and you clearly define the interface of the contained, without risking unintended side effects.

So, in the end, the question stands: what's the point of inheritance in Python?

Edit: thanks for the very interesting answers. Indeed you can use it for code reuse, but I am always careful when reusing implementation. In general, I tend to do very shallow inheritance trees or no tree at all, and if a functionality is common I refactor it out as a common module routine and then call it from each object. I do see the advantage of having one single point of change (eg. instead of adding to Dog, Cat, Moose and so on, I just add to Animal, which is the basic advantage of inheritance), but you can achieve the same with a delegation chain (eg. a la JavaScript). I'm not claiming it's better though, just another way.

I also found a similar post on this regard.

+7  A: 

I think the point of inheritance in Python is not to make the code compile, it is for the real reason of inheritance which is extending the class into another child class, and to override the logic in the base class. However the duck typing in Python makes the "interface" concept useless, because you can just check if the method exist before invokation with no need to use an interface to limit the class structure.

bashmohandes
Selective overriding is the reason for inheritance. If you're going to override everything, that's a weird special case.
S.Lott
Who would override everything? you can think of python like all methods are public and virtual
bashmohandes
@bashmohandes: I would never override everything. But the question shows a degenerate case where everything gets overridden; this weird special case is the basis for the question. Since it never happens in normal OO design, the question is kind of pointless.
S.Lott
+45  A: 

You are referring to the run-time duck-typing as "overriding" inheritance, however I believe inheritance has its own merits as a design and implementation approach, being an integral part of object oriented design. In my humble opinion, the question of whether you can achieve something otherwise is not very relevant, because actually you could code Python without classes, functions and more, but the question is how well-designed, robust and readable your code will be.

I can give two examples for where inheritance is the right approach in my opinion, I'm sure there are more.

First, if you code wisely, your makeSpeak function may want to validate that its input is indeed an Animal, and not only that "it can speak", in which case the most elegant method would be to use inheritance. Again, you can do it in other ways, but that's the beauty of object oriented design with inheritance - your code will "really" check whether the input is an "animal".

Second, and clearly more straightforward, is Encapsulation - another integral part of object oriented design. This becomes relevant when the ancestor has data members and/or non-abstract methods. Take the following silly example, in which the ancestor has a function (speak_twice) that invokes a then-abstract function:

class Animal(object):
    def speak(self):
        raise NotImplementedError()

    def speak_twice(self):
        self.speak()
        self.speak()

class Dog(Animal):
    def speak(self):
        print "woff!"

class Cat(Animal):
    def speak(self):
        print "meow"

Assuming "speak_twice" is an important feature, you don't want to code it in both Dog and Cat, and I'm sure you can extrapolate this example. Sure, you could implement a Python stand-alone function that will accept some duck-typed object, check whether it has a speak function and invoke it twice, but that's both non-elegant and misses point number 1 (validate it's an Animal). Even worse, and to strengthen the Encapsulation example, what if a member function in the descendant class wanted to use "speak_twice"?

It gets even clearer if the ancestor class has a data member, for example "number_of_legs" that is used by non-abstract methods in the ancestor like "print_number_of_legs", but is initiated in the descendant class' constructor (e.g. Dog would initialize it with 4 whereas Snake would initialize it with 0).

Again, I'm sure there are endless more examples, but basically every (large enough) software that is based on solid object oriented design will require inheritance.

Roee Adler
For the first case, it would mean that you are checking types instead of behavior, which is kind of unpythonic.For the second case, I agree, and you are basically doing the "framework" approach. You are recycling the implementation of speak_twice, not only the interface, but for overriding, you can live without inheritance when you consider python.
Stefano Borini
You can live without many things, like classes and functions, but the question is what makes code great. I think inheritance does.
Roee Adler
@Stefano Borini - It sounds like you're taking a very "rules-based" approach. The old cliche is true though: they were made to be broken. :-)
Jason Baker
upvoted for the eloquence of the first paragraph
Shawn Simon
@Jason Baker - I tend to like rules because they report wisdom gained from experience (eg. mistakes), but I don't like creativity to be hindered by them. So I agree with your statement.
Stefano Borini
I don't find this example so clear - animals, cars and shape examples really suck for those dicussions :) The only thing which matters IMHO is whether you want to inherit implementation or not. If so, rules in python are really similar to java/C++; the difference is mostly for inheritance for interfaces. In that case, duck-typing is often the solution - much more so than inheritance.
David Cournapeau
@Rax : I totally agree. Python has this clear advantage: easy, simple code. I would not pollute it for anything in the world.
Stefano Borini
A: 

Classes in Python are basically just ways of grouping a bunch of functions and data.. They are different to classes in C++ and such..

I've mostly seen inheritance used for overriding methods of the super-class. For example, perhaps a more Python'ish use of inheritance would be..

from world.animals import Dog

class Cat(Dog):
    def speak(self):
        print "meow"

Of course cats aren't a type of dog, but I have this (third party) Dog class which works perfectly, except the speak method which I want to override - this saves re-implementing the entire class, just so it meows. Again, while Cat isn't a type of Dog, but a cat does inherit a lot of attributes..

A much better (practical) example of overriding a method or attribute is how you change the user-agent for urllib. You basically subclass urllib.FancyURLopener and change the version attribute (from the documentation):

import urllib

class AppURLopener(urllib.FancyURLopener):
    version = "App/1.7"

urllib._urlopener = AppURLopener()

Another manner exceptions are used is for Exceptions, when inheritance is used in a more "proper" way:

class AnimalError(Exception):
    pass

class AnimalBrokenLegError(AnimalError):
    pass

class AnimalSickError(AnimalError):
    pass

..you can then catch AnimalError to catch all exceptions which inherit from it, or a specific one like AnimalBrokenLegError

dbr
I'm… a bit confused by your first example. Last I checked, cats aren't a kind of dog, so I'm not sure what relationship you're trying to demonstrate. :-)
Ben Blank
You are messing with the Liskov principle: the Cat IS NOT a Dog. It may be OK to use in this case, but what if the Dog class changes and get, for example, a "Lead" field, which is senseless for cats?
Dmitry Risenberg
Well if there is no Animal base-class, your alternative is to reimpliment the whole thing.. I'm not saying it's the best practice (if there's an Animal base-class, use it), but it works and is used commonly (it's the recommended way of changing the user-agent of urllib, as per the example I added)
dbr
+11  A: 

In your second example of Python, how do you propose that all instances of Dogs and Cats will be able to speak()? Your first example in Python, you're at least trapping this. However, your program can stop under the right circumstances, which may or may not be what you want.

Also, why not consider doing this:

class Animal(object):
    def speak(self):
        raise NotImplementedError()

class Dog(Animal):
    def speak(self):
        print "woff!"

class Cat(Animal):
    def speak(self):
        print "meow"

d=Dog()
c=Cat()

# Apply the method directly.
d.speak()
c.speak()

The raise in there can cause "issues" in your code, although as I said earlier, maybe you don't want that to happen. So let's extend this even further:

class Animal(object):
    def __init__(self):
        self.canSpeak = False
    def speak(self, sound):
        if self.canSpeak == True:
            print sound

class Dog(Animal):
    def speak(self, sound="Woof!"):
        Animal.speak(self, sound)

class Cat(Animal):
    def __init__(self):
        self.canSpeak = True
    def speak(self, sound="Meow!"):
        Animal.speak(self,sound)

d=Dog()
c=Cat()

# Apply the method directly.
d.speak()
c.speak()

Ah-ha! Now your Cat can speak, your Dog won't, but calling speak() for a Dog won't raise an exception because your inheriting speak from Animal, which implements speak() for both Dog and Cat. But guess what? Goose and Moose drop by and want to have their say too! So here's what we have now...

class Animal(object):
    def __init__(self):
        self.canSpeak = False
    def speak(self, sound):
        if self.canSpeak == True:
            print sound

class Dog(Animal):
    def speak(self, sound="Woof!"):
        Animal.speak(self, sound)

class Cat(Animal):
    def __init__(self):
        self.canSpeak = True
    def speak(self, sound="Meow!"):
        Animal.speak(self,sound)

class Goose(Animal):
    def __init__(self):
        self.canSpeak = True
    def speak(self, sound="Honk!"):
        Animal.speak(self,sound)

class Moose(Animal):
    def __init__(self):
        self.canSpeak = True
    def speak(self, sound="Muhhhh!"):
        Animal.speak(self,sound)

d=Dog()
c=Cat()
g=Goose()
m=Moose()

# Apply the method directly.
d.speak()
c.speak()
g.speak()
m.speak()

Notice that the code is fairly deterministic, as long as it's executing within the scope of the object - .canSpeak is always set to some value, and .speak() is always valid. So, it's up to you if you really want to have that raise show up at some point, or if you just want to keep truck'in along by just coding in advance a common method. Using the older code you had, you would have to grind out a speak for your Goose or Moose, or worse, if you forgot to crank out a speak(), the whole thing would grind to a halt; but the new code brings speak() along for free via inheritance. So now Goose, Moose, Cat, and Dog can all speak() (well, sorta, the Dog still needs attention from the Vet^H^H^HCoder, and we're assuming that Goose and Moose will get their .canSpeak set when they __init__() ).

Inheritance is just another tool, and given that everything in Python - even string literals - are objects, you'll have some need at some time to use this feature.

Avery Payne
+3  A: 

In C++/Java/etc, polymorphism is caused by inheritance. Abandon that misbegotten belief, and dynamic languages open up to you.

Essentially, in Python there is no interface so much as "the understanding that certain methods are callable". Pretty hand-wavy and academic-sounding, no? It means that because you call "speak" you clearly expect that the object should have a "speak" method. Simple, huh? This is very Liskov-ian in that the users of a class define its interface, a good design concept that leads you into healthier TDD.

So what is left is, as another poster politely managed to avoid saying, a code sharing trick. You could write the same behavior into each "child" class, but that would be redundant. Easier to inherit or mix-in functionality that is invariant across the inheritance hierarchy. Smaller, DRY-er code is better in general.

agileotter
+6  A: 

I think that it is very difficult to give a meaningful, concrete answer with such abstract examples...

To simplify, there are two types of inheritance: interface and implementation. If you need to inherit the implementation, then python is not so different than statically typed OO languages like C++.

Inheritance of interface is where there is a big difference, with fundamental consequences for the design of your software in my experience. Languages like Python does not force you to use inheritance in that case, and avoiding inheritance is a good point in most cases, because it is very hard to fix a wrong design choice there later. That's a well known point raised in any good OOP book.

There are cases where using inheritance for interfaces is advisable in Python, for example for plug-ins, etc... For those cases, Python 2.5 and below lacks a "built-in" elegant approach, and several big frameworks designed their own solutions (zope, trac, twister). Python 2.6 and above has ABC classes to solve this.

David Cournapeau
+4  A: 

Inheritance in Python is more of a convenience than anything else. I find that it's best used to provide a class with "default behavior."

Indeed, there is a significant community of Python devs who argue against using inheritance at all. Whatever you do, don't just don't overdo it. Having an overly complicated class hierarchy is a sure way to get labeled a "Java programmer", and you just can't have that. :-)

Jason Baker
+5  A: 

Inheritance in Python is all about code reuse. Factorize common functionality into a base class, and implement different functionality in the derived classes.

Roberto Bonvallet
+1  A: 

You can get around inheritance in Python and pretty much any other language. It's all about code reuse and code simplification though.

Just a semantic trick, but after building your classes and base classes, you don't even have to know what's possible with your object to see if you can do it.

Say you have d which is a Dog that subclassed Animal.

command = raw_input("What do you want the dog to do?")
if command in dir(d): getattr(d,command)()

If whatever the user typed in is available, the code will run the proper method.

Using this you can create whatever combination of Mammal/Reptile/Bird hybrid monstrosity you want, and now you can make it say 'Bark!' while flying and sticking out its forked tongue and it will handle it properly! Have fun with it!

mandroid
+1  A: 

I don't see much point in inheritance.

Every time I have ever used inheritance in real systems, I got burned because it led to a tangled web of dependencies, or I simply realised in time that I would be a lot better off without it. Now, I avoid it as much as possible. I simply never have a use for it.

class Repeat:
    "Send a message more than once"
    def __init__(repeat, times, do):
        repeat.times = times
        repeat.do = do

    def __call__(repeat):
        for i in xrange(repeat.times):
             repeat.do()

class Speak:
    def __init__(speak, animal):
        """
        Check that the animal can speak.

        If not we can do something about it (e.g. ignore it).
        """
        speak.__call__ = animal.speak

    def twice(speak):
        Repeat(2, speak)()

class Dog:
     def speak(dog):
         print "Woof"

class Cat:
     def speak(cat):
         print "Meow"

>>> felix = Cat()
>>> Speak(felix)()
Meow

>>> fido = Dog()
>>> speak = Speak(fido)
>>> speak()
Woof

>>> speak.twice()
Woof

>>> speak_twice = Repeat(2, Speak(felix))
>>> speak_twice()
Meow
Meow

James Gosling was once asked at a press conference a question along the lines: "If you could go back and do Java differently, what would you leave out?". His response was "Classes", to which there was laughter. However, he was serious and explained that really, it was not classes that were the problem but inheritance.

I kind of view it like a drug dependency - it gives you a quick fix that feels good, but in the end, it messes you up. By that I mean that it is a convenient way to reuse code, but it forces an unhealthy coupling between child and parent class. Changes to the parent may break the child. The child is dependant on the parent for certain functionality and cannot alter that functionality. Therefore the functionality provided by the child is also tied to the parent - you can only have both.

Better is to provide one single client facing class for an interface which implements the interface, using the functionality of other objects which are composed at construction time. Doing this via properly designed interfaces, all coupling can be eliminated and we provide a highly composable API (This is nothing new - most programmers already do this, just not enough). Note that the implementing class must not simply expose functionality, otherwise the client should just use the composed classes directly - it must do something new by combining that functionality.

There is the argument from the inheritance camp that pure delegation implementations suffer because they require lots of 'glue' methods which simply pass along values through a delegation 'chain'. However, this is simply reinventing an inheritance-like design using delegation. Programmers with too many years of exposure to inheritance-based designs are particularly vulnerable to falling into this trap, as, without realising it, they will think of how they would implement something using inheritance and then convert that to delegation.

Proper separation of concerns like the above code doesn't require glue methods, as each step is actually adding value, so they are not really 'glue' methods at all (if they don't add value, the design is flawed).

It boils down to this:

  • For reusable code, each class should do only one thing (and do it well).

  • Inheritance creates classes that do more than one thing, because they are mixed up with parent classes.

  • Therefore, using inheritance makes classes that are hard to reuse.

A: 

It's not inheritance that duck-typing makes pointless, it's interfaces — like the one you chose in creating an all abstract animal class.

If you had used an animal class that introduce some real behavior for its descendants to make use of, then dog and cat classes that introduced some additional behavior there would be a reason for both classes. It's only in the case of the ancestor class contributing no actual code to the descendant classes that your argument is correct.

Because Python can directly know the capabilities of any object, and because those capabilities are mutable beyond the class definition, the idea of using a pure abstract interface to "tell" the program what methods can be called is somewhat pointless. But that's not the sole, or even the main, point of inheritance.

Larry Lustig