Programmer's Python - Single Inheritance
Written by Mike James   
Monday, 06 August 2018
Article Index
Programmer's Python - Single Inheritance
Basic Inheritance
Overriding
Super Super and the mro

Classes in Python aren’t very encapsulated.

You can generally access any attribute in a class and call any method including those that are nominally private.

The idea of objects is just a matter of packaging the data with the function that processes them. In Chapter 6 we discovered that the key was the change from:

function(data,parameters)

to:

data.function(parameters)

This is simple and has obvious very direct advantages. This small change means that we think about data and functions as objects that “know” how to process the data they store. When you write mydata.sort() you are asking the data to sort itself using a method that belongs to it, and presumably knows how to sort this particular data.

That the operation changes according to the object is the basis of polymorphism, the remaining big idea of object-oriented programming.

Object-oriented programming is generally, but not always, taken to mean:

  • the use of classes or object factories to create instances. The use of classes also gives us a way of extending the type system – more of this in Chapter 13.

  • encapsulation to keep the inner workings of objects private and to define a public interface that remains unchanged over time.

  • inheritance to allow the extension of existing classes to new classes without having to start from scratch.

  • Polymorphism, where operations are dependent on the class that they are applied to.

Object-Oriented Philosophies

What has been described so far is essentially the mechanisms of object-oriented programming. They are what you expect the language to provide. In addition to these basic mechanisms there is also a substantial theory relating to how you should use objects.

The key idea is that objects in a program should model the outside world or the abstractions that they represent. This is the domain model and you will find many standard examples of object-oriented programming introduced using toy domain models.

Perhaps the most common is the animal model and it is based on the isA relationship. A Dog is an Animal. A Cat is an Animal. This is taken to be the fundamental defining structure of the domain model and this is to be captured in the object or rather class structure of the program. So we have an Animal class, a Dog class which inherits from Animal, and a Cat class which inherits from Animal. The Animal class would have attributes common to all animals and the Dog and Cat classes would have methods specific to each.

This sort of toy example is very encouraging because it is small and manageable. In the real world class hierarchies quickly become deep and uncontrollable. Developing such models generally reveals that we got the structure wrong in some way. For example, where does a Virus or a Bacteria class fit in? And if we have a Horse and a Donkey class, where does Mule fit? After all a Mule is a sort of horse and a sort of Donkey.

The point is that real world domains are rarely strictly hierarchical. Things in the real world are often more than one thing at a time.

These considerations cause many problems and even cause programmers to doubt that object-oriented programming is worth using – but it is. Forget for a moment about capturing the supposed hierarchies in the real world and just concentrate on what an object is. If we have a Car class then it is fairly obvious what sorts of attributes it should have – or it is most likely to be in a real situation. It doesn’t matter if a Car gets its steering wheel attribute from a Truck class or from some abstracted Vehicle class – as long as it has a steering wheel. It also doesn’t matter if the road wheels and the steering wheels derive from a more basic abstract wheel class or an even more abstract circle class.

The object is the important construct and how it is constructed is another issue.

In the rest of this chapter we restrict our ambitions just to inheritance as a way of reusing code rather than modeling the world.

Basic Inheritance

To allow a subclass to inherit from a base class all you have to do is include the base class in the class definition:

class base:
   def myMethodBase(self):
     print("Base method")

class child(base): pass

In this case the child class inherits from the base class.

What happens when an instance of the child class attempt to retrieve an attribute is just an extension of what happens when there is no inheritance.

For example:

childObject=child()
childObject.myMethodBase()

Now when myMethodBase is retrieved, first the instance childObject.__dict__ is checked. When it isn’t found there, the class child.__dict__ is checked and when it isn’t found there, the base class base.__dict__ is checked, where it is finally found and called to print “Base method”.

That is, in general if the attribute isn’t found in the instance object the dictionary of the class that created it is searched, if it isn’t found there then the first base class is searched, and if it isn’t found there its base class is searched, and so on up the inheritance chain until a definition is found or the lookup fails. The first class in the hierarchy that has the attribute defined supplies it in the usual way.

Notice that self is set to childObject no matter which class finally returns myMethodBase. What this means is that if myMethodBase uses say self.myAttribute then this is now a reference to childObject.myAttribute and it is resolved in the usual way by looking in the instance, class and finally base class __dict__.

That is, myMethodBase is executed as if it was a method of childObject.

If you are familiar with languages such as C++ it is worth saying that all methods in Python are “virtual” in the sense that they are always methods of the calling object. If myMethodBase calls another method using self.anotherMethod it is childObject.anotherMethod that is called and not base.anotherMethod.

<ASIN:1871962587>



Last Updated ( Monday, 06 August 2018 )