Oo Programming The Python Way
Posted on 20 Sep 1999
by Dody Gunawinata (nirataro)
Rated 3.93 (Ratings: 3)
- More articles in Backend
Unrelevant mattersOk, 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
expletiveis 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 ?
import stringNow 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.splitsplit('Split;This',';')Another way is to use explicit function import:
from string import split, findThis 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 moduleCreate 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"and save it as
def PrintMyAge():print "Your age"
def _PrintMe():print "This can't be used"
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:
#This will result in errormyLib._PrintMe()
Wacky classMany 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 : passThis 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"Notice that you are actually assigning a new variable to an object. Of course you can retrieve it again:
p.Age = 20
print "My name is",p.Name,"and my age is",p.AgeHow about function ? Well, you can do it as well:
def Address()print "Omaha, Nebraska, USA"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
p.Address = Address
#then use it as usualp.Address()
patchingthem 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 assignmentContinuing the previous example, if you assign
pto 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.AgeChanging the Age attributes thru m changes the object it points to. Because
ppoints to the same object, of course the printed value is the same.
Copy by valuePython 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 CopyThis 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
childobject. 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 CopyLike 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
childobject 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 : constructorThis is how you declare constructor in Python classes:
class Person:def __init__(self): self.Name = 'Me'self.Age = 20I 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 = AgeThe 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 publicThe 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 - LieNow, 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.
PolymorphismPeople 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"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
#Create empty listlist = [ ]
#Add objects to the listlist.append (Monkey()) list.append (Car()) list.append (House())
#Iterates on the listfor x in list: x.show() #The loop prints #"This is monkey" #"This is a car" #"This is a house"
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).