Programming Python, Part II

 in
Having covered some advanced features in Part I, it's time to include some basics.
Inheritance

The short introduction to object-oriented programming (OOP) in Part I of this article left out a big topic—inheritance. This feature is what makes OOP really useful, and as OOP tries to mimic real life, I explain inheritance here with real-life examples.

Think about a chair. A chair is made out of some kind of material, has two armrests, a back, a color, a style and maybe even a warranty. Now, think about a table. It is made out of some kind of material, might have some drawers, a color, a style and maybe a warranty. They have a lot in common! If we were to make the two classes, Chair and Table, a lot of code would be repeated. In programming, when you write the same line of code twice, you probably are doing something wrong—inheritance to the rescue.

A chair is a piece of furniture. So is a table. Such similarities can be in the Furniture class. Let's make the Furniture class have a default material and the ability to set other materials:

class Furniture(object):
    def __init__(self):
        self._material = "wood"
    
    def set_material(self, material):
        self._material = material

And now, a Chair class inheriting Furniture:

class Chair(Furniture):
    def __init__(self):
        self._backrest_height = 30
    
    def set_backrest_height(self, height):
        self._backrest_height = height

Now, you know what goes inside parentheses in the class header: the name of the class being inherited, which also is known as a super class or parent class. Let's play a bit with this, so you can see what happens:

>>> c = Chair()
>>> c.set_backrest_height(50)
>>> c._backrest_height
50
>>> c.set_material("plastic")
>>> c._material
'plastic'
>>>

As you can see, the methods of Furniture also are on Chair. I leave the definition of the Table class as an exercise for the reader. But first, here's another interaction:


>>> d = Chair()
>>> d._backrest_height
30
>>> d._material
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
AttributeError: 'Chair' object has no attribute '_material'
>>>

I bet that is not what you expected. Let's take a closer look at what happened. We created a Chair, the method Chair.__init__ was run setting _backrest_height. Oh! Nobody called Furniture.__init__, which never set _material. There are two solutions to that.

Setting _material in Chair.__init__ is not a solution. If we do that, the classes would be coupled, meaning the implementation of one will depend on the implementation of the other. If we change the name of _material to _materials, suddenly Chair will stop working. If you have hundreds of classes developed by hundreds of different people, keeping track of those changes is difficult. Also, Furniture will grow to have more members, so we have to remember to set all those members to the same defaults in Chair.__init__. I'm getting a headache just thinking about it.

One real solution is calling Furniture.__init__ and rewriting Chair.__init__ this way:

def __init__(self):
    Furniture.__init__(self)
    self._backrest_height = 30

We had to pass self to __init__, because if we called it with the class instead of the object, it wouldn't know in which object to do its operations.

I personally don't like that solution, because it implies writing the name of the class in two or more places. If you ever change the name, you'll have to remember to run a search and replace. Another solution is more cryptic than it should be, but it doesn't have the problem I just mentioned:

def __init__(self):
    super(Chair, self).__init__()
    self._backrest_height = 30

In this solution, I call super, passing the current class and the current object, and it allows me to make a call to the parent class using the current object. Here we may have a problem if we change the name of the class itself, but running a search and replace on the file is a good idea when making that kind of change. You'd want to change the documentation as well. The real problem with this solution is hard to understand and to explain—it has to do with multiple inheritance. For more information, read “Python's Super Considered Harmful”. Personally, I've been using this second solution without any problems.

You'll see that all classes I defined inherit from object. That is the most basic class—the root (or top) class. It is a good idea to make all your classes inherit from it unless they inherit from another class. If you don't do that, your class will be an old-style class, and some things won't work, such as super. It is important to know this, because you may encounter old-style classes anywhere, and you should be prepared.

______________________

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

error?

csshef's picture

cool = blog.Post("Cool", "Python is cool")

I can't get the above line to work. Is that correct?

error?

ejournal's picture

Were you using Emacs and following the instructions in part I? If so, the class "Post" will be part of the module "blog". If not, you must arrange for that yourself - the article explains.

If I understand correctly. I'm not a Python programmer. :)

White Paper
Linux Management with Red Hat Satellite: Measuring Business Impact and ROI

Linux has become a key foundation for supporting today's rapidly growing IT environments. Linux is being used to deploy business applications and databases, trading on its reputation as a low-cost operating environment. For many IT organizations, Linux is a mainstay for deploying Web servers and has evolved from handling basic file, print, and utility workloads to running mission-critical applications and databases, physically, virtually, and in the cloud. As Linux grows in importance in terms of value to the business, managing Linux environments to high standards of service quality — availability, security, and performance — becomes an essential requirement for business success.

Learn More

Sponsored by Red Hat

White Paper
Private PaaS for the Agile Enterprise

If you already use virtualized infrastructure, you are well on your way to leveraging the power of the cloud. Virtualization offers the promise of limitless resources, but how do you manage that scalability when your DevOps team doesn’t scale? In today’s hypercompetitive markets, fast results can make a difference between leading the pack vs. obsolescence. Organizations need more benefits from cloud computing than just raw resources. They need agility, flexibility, convenience, ROI, and control.

Stackato private Platform-as-a-Service technology from ActiveState extends your private cloud infrastructure by creating a private PaaS to provide on-demand availability, flexibility, control, and ultimately, faster time-to-market for your enterprise.

Learn More

Sponsored by ActiveState