Skip to page content or Skip to Accesskey List.

Work

Main Page Content

Oo Programming The Python Way

Rated 3.93 (Ratings: 3)

Want more?

 

Dody Gunawinata

Member info

User since: 23 Aug 1999

Articles written: 5

Unrelevant matters

Ok, now we are in part two, the sequel, the continuation, the second awakening. We are moving closer to discussing Zope, the coolest web application server around. I actually type this article in Zope-structured format (which is just an ASCII file with indentation rules) and let Zope render it as an HTML file. There is no need for me to worry about HTML tags. Check out at Zope.org and don't forget to take the picture of your face with your jaw dropped.

The last article gives you the absolute basics of using Python. This time, we'll do the OO side of Python. Yes, Python: a true object-oriented language with classes, inheritance and all.

Ok, my OO background comes from the usual suspects of OO language, C++, Java and Object Pascal (Delphi). So I have this preconceived idea about what Python OO should be. When I discover how OO is implemented in Python, my first reaction was: "What the expletive is this s#~! ?" My idealism about OO is offended by what Python does with object and classes. It offers a new perspective on what can be done with classes and object. Over time Python has grown on me, and now I can really appreciate what it can do. So if you have the same reaction as mine, keep reading. It will grow on you as well.

We are going to tear down and really push object today. Have your seat belt on?

This is what you get in a Python class. (I assume you already know OO. If not, get a decent book on it, as it's very important to understand the concept. If you code exclusively in VB, learn another language, you haven't used OO yet.)

These are some of the OO goodies that come with Python:

  • No explicit support for private, protected or public method and data member
  • Multiple Inheritance
  • All methods are virtual
  • Run time object modification
  • Ability to create your object, and forget about it. The system takes care of it, just like Java (albeit with different mechanism -- Python uses reference counting instead of garbage collector)
  • Polymorphism -- well of course you get it, although it's different from what you might think
  • Easy-to-use persistency support
  • How about generic object container that can contain objects of any kind ?

But before dwelling on the class, let's talk briefly about using external libraries.
import string

Now you can use all the function in string module. Check the library reference that comes with your Python installation for more external function libraries:
string.split('Split;This',';')

You need to qualify them. I usually use this notation if I don't use the function often in my code. Otherwise, you can always use function aliasing (remember the previous article):

split = string.split

split('Split;This',';')

Another way is to use explicit function import:
from string import split, find

This imports the function explicitly so you can use without the module prefix:
split('Split;This',';')

Of course, you can just import the whole lot of them. Now you can use all the exportable function from string module without using module prefixes. Remember the word "exportable":
from string import *

Create your own module

Create your collection of functions and put them in a file. All functions defined in the file where names aren't started with _ prefix are exportable. To keep the function "private" in the module, add _ (underscore) prefix in front of the function name:

def PrintMyName():

print "My Name"

def PrintMyAge():

print "Your age"

def _PrintMe():

print "This can't be used"

and save it as myLib.py. Then use it like this. See that the file name becomes the module name. That applies for other libraries as well. The string library would be stored in a file called string.py. You can find it in your (python directory)/bin/lib directory:

import myLib

myLib.PrintMyName() myLib.PrintMyAge()

#This will result in error

myLib._PrintMe()

Wacky class

Many papers uses the terms class and class instance. I don't like them because they can be hard to understand. I will use classes and objects instead in this article. Class is well, class, and object is what you create from the class. Object is run-time.

This is how you declare an empty class. The "pass" is a keyword which does nothing. You use it just to meet the requirements of a construct that requires at least one statement:
class Person : pass

This is the simplest form of class.

Then to be useful, you should make the object out of it:
p = Person()

Of course, the object does nothing at this point. You are about to see the "run time object modification" features of Python.

Now you can declare a new variable for the object:
p.Name = "John"
p.Age = 20

Notice that you are actually assigning a new variable to an object. Of course you can retrieve it again:
print "My name is",p.Name,"and my age is",p.Age

How about function ? Well, you can do it as well:

def Address()

print "Omaha, Nebraska, USA"

p.Address = Address

#then use it as usual

p.Address()

It would be the end of the world before Java allows you to do this. Imagine what you can do with this feature. How about creating a class factory that keeps enhancing the currently live object by patching them with more and more data and attributes (or delete them)? Genetic programming, anyone ? You can also use the class to act like record or structure to store data. Or as a more efficient variant records (Object Pascal) or union (C/C++).

Object assignment

Continuing the previous example, if you assign p to another variable, you are actually copying the reference to the object, not the copy of the object itself. The following example should make it clear:

m = p

m.Age = 30

print p.Age,"is the same as",m.Age

Changing the Age attributes thru m changes the object it points to. Because p points to the same object, of course the printed value is the same.

Copy by value

Python does not support copy by value in the language. But there is a module which provides the facility for object copy. It's the creatively-named copy module.

The module provides two type of copies:

Shallow Copy

This will create a new object and copy the value of the copied object to this new object. However, if the copied object has a reference to another object (aggregation) , the new object would get the reference to this child object. So in the end, you would have two object that points to the same child object:

class Parent :

def __init__(self,name): self.name = name

def HasChild (self,child): self.child = child

class Child : def __init__(self,name): self.name = name

A = Parent("John") A.HasChild (Child("David"))

import copy

B = copy.copy (A)

B.name = "Marco" B.child.name = "Emmanuella"

print B.name, 'has a child named',B.child.name

print A.name, 'also has a child named',A.child.name

Deep Copy

Like shallow copy, it creates a new object and copies the value from the copied object to the new object. For child object, the child object is recreated for the new object. So in the end, the new object would get a child object of its own, instead of just getting the object reference. This way, you got two object with two child object:

B = copy.deepcopy (A)

B.name = "Marco" B.child.name = "Emmanuella"

print B.name, 'has a child named',B.child.name

print A.name, 'has a child of different name',A.child.name

Proper class : constructor

This is how you declare constructor in Python classes:

class Person:

def __init__(self): self.Name = 'Me'

self.Age = 20

I bet the first thing you ask is, "What is self"? Python class requires every method to have at least one argument to pass the object itself implicitly. So if you use __init__() it will actually generate an error. Python uses the first argument for passing the object itself to the methods. The convention is to use self as the name of this argument. You can use another name for this, but it would make life easier for you and others if you stick with the convention self.

In Python, there is no method overloading.

The statement self.Name and self.Age creates object data member. In other words, this data member would be created exclusively for each object, as opposed to class data member, where the values are shared by all objects. Let's add some more new stuff:

class Person:

def __init__(self,Name='Me',Age=20):

self.Name = Name self.Age = Age

def AboutMe(self):

print 'My name is',self.Name,'and I am',self.Age,'old'

def SetName(self, Name): self.Name = Name

def SetAge (self, Age): self.Age = Age

The class is more complete right now. Notice that I use default arguments for the constructor. Let's see how we use it:

me = Person('Dody',21) you = Person()

me.AboutMe() you.AboutYou()

you.SetName("Bobby Brown") you.SetAge (30)

me.AboutMe() you.AboutMe()

You see that we don't actually pass something to the AboutMe although the method has one argument. That's what I mean by saying Python uses the first argument implicitly to pass the object to the methods. So you can ignore it.

It's public

The SetAge and SetName method in Person class in the previous example is redundant. Why? because the object data member Name and Age is public. So you can directly access it using, using the previous object creation:

me.Name = "John Kaster" me.Age = 31

me.AboutMe()

To make it private, use the __ (double underscores) trick. This trick also applies for method as well:

class Person:

def __init__(self,Name='Me',Age=20): self.__Name = Name self.__Age = Age

def AboutMe(self):

print 'My name is',self.__Name,'and I am',self.__Age,'old'

def SetName(self, Name): self.__Name = Name

def SetAge (self, Age): self.__Age = Age

def __LieMyAge (Lie='5') self.__Age = self.__Age - Lie

Now, you cannot access the Name and Age data member directly, and hat applies to LieMyAge as well. So this won't work:

me = Person()

me.__Name = 'Dody'

me.__LieMyAge()

me.AboutMe()

But off course it's all a scam, a deceit, a lie. Do you know what the __ does to your variable/methods ? Python interpreters actually do a name mangling with anything that has double underscores, in the form of _classname__var/function name. That's why "me.__Name" or "me.__LieMyAge()" doesn't work, because they don't exist as far as Python is concerned. So the below example will actually work:

me = Person()

me._Person__Name = 'Dody'

me._Person__LieMyAge()

me.AboutMe()

Of course, you need to be determined to access those attributes, but again it is all possible with Python. Being able to use it doesn't mean that you have to use it. Use your own judgment as to when it is appropriate.

Polymorphism

People would laugh if someone actually talked about Polymorphism without explaining inheritance first. But I can do this in Python due to the way Python handles variables. Python variables do not have a type, so the traditional concept of Polymorphism where a parent class variable can call it's children methods doesn't exist or make a difference. I can use one variable to hold any kind of objects. No matter what kind of object the variable is referring too, as long as the object has the method name I call, everything is OK. Object type is not important. Virtual methods make no difference in terms of Polymorphism in Python, but it does have an effect in inheritance:

class Monkey:

def show(self):

print "This is monkey"

class Car:

def show(self):

print "This is a car"

class House:

def show(self):

print "This is a house"

#Create empty list

list = [ ]

#Add objects to the list

list.append (Monkey())

list.append (Car())

list.append (House())

#Iterates on the list

for x in list:

x.show()

#The loop prints

#"This is monkey"

#"This is a car"

#"This is a house"

The above example uses the usual algorithm other books use to demonstrate polymorphism. It works simpler in Python. However, you probably missed a subtle thing that the example shows. It is that the list can hold three object of different type and call the methods without any typecasting at all. Generic object containers are the holy grails of other OO languages. You can't do this directly in C++, Object Pascal or even in Java. In Java at least you can use list-type interfaces, which the objects have to support. In C++ or Object Pascal, the list has to be the parent type of those objects, which is more restrictive than an interface. Can you see what Python offers for the OO world?

The statement x.show() is an exception if the current object referred by x does have show() method. However, you can detect whether the object has a particular method by using the built-in function hasattr(obj,name):

if hasattr(x,'show'):

# or

if hasattr(Monkey(),'show'):

You can use this before inserting the object into the list or just before executing the method show(). Notice you only insert the name, not the arguments (this is possible because there is no method overloading in Python).

Inheritance

Inheritance and multiple inheritance will be discussed in the next article, including some other aspects of OO in Python. We will dissect every line of code of the httplib object and see any way we can extend it using Python inheritance. You would be suprised how simple it is. And before I forget, some discussion about tuples will also be included. Until that time, enjoy your Python.

The access keys for this page are: ALT (Control on a Mac) plus:

evolt.org Evolt.org is an all-volunteer resource for web developers made up of a discussion list, a browser archive, and member-submitted articles. This article is the property of its author, please do not redistribute or use elsewhere without checking with the author.