[ZDP] ZCL-Python Content Tree release

Dody Gunawinata dody_g@eles.com
Tue, 05 Oct 1999 11:55:54 +1000


This is a multi-part message in MIME format.
--------------BB30EF5D058124AC829991A1
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

Week 01. [ZCL]

Please find attached the first release of the Python Topic
implementation (sounds like programming :).
I promised that I would release it on Monday, well, I guess it's still
Monday in Timbuktu :)

There are several [ToDO] list in the document that I would complete
within 2 or 3 days. So please direct
your comment on what's already been done. 

For feedback, "Yeah it's great" response is good for my ego but not for
the docs, so please pint point the good, preferably the bad thing,
so I know which part of the doc to improve. On the other hand, feedback
like "I'm a rocket scientist and I couldn't understand a thing about
this damn docs" does help either (you probably don't speak english
sir/ma, :)

Please give your feedback or at least read thru it :).

Dody
--------------BB30EF5D058124AC829991A1
Content-Type: text/plain; charset=us-ascii;
 name="Releasable.txt"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="Releasable.txt"

[todo-chapter intro-DG-99/10/2]
[todo-variables-DG-99/10/2]
[todo-ifs/elses/-DG-99/10/2]
[todo-loops-DG-99/10/2]
[todo-tuples-DG-99/10/2]
[todo-function-DG-99/10/2]

[todo-list-DG-99/10/2]


Using external modules/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 ::

  split = string.split
  split('Split;This',';')


Another way is to use explicit function import:

  from string import split, find


This import the function explicitly so you can use without the module prefix.:

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


Off course you can just import the whole thing. Now you can use all the exportable function from string module without using module prefixes. Remember the word "exportable":

  from string import *

  split ('I want this;I want that',';')


Creating modules/libraries

Create your collection of functions and put them in a file. All functions defined in the file which 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 (as an example). Then use it like below example. 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()


[to do--search path in python-DG-99/10/2]


OO:Class and defining object

It is assumed that you have at least a basic understanding of object oriented programming. If not, the rest of this
OO discussion would be meaningless to you.

These are some of the Object Oriented programming characteristic and supports that comes with Python :

	-No explicit support for private, protected or public method and data member.

	-Inheritance, including Multiple Inheritance.

	-All methods are virtual.
	
	-Run time object modification. Sorry, I couldn't find a better terms for this. You will find out.

	-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 off course you get it, although it's different from what you might think.

	-Easy to use persistency support.

	-Generic object container that can contain objects of any kind.


Basic class

Many papers uses the terms class and class instance. I don't use them here 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 do 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 (instantiate it):

  p = Person()


Off course the object does nothing at this point. Wait, 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 features. 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 change the object it points to. Because p points to the same object, off course the printed value is the same.


Copy by value

Python does not support copy by value in the language. But, no fear, 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 valued 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 generates 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 other 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 objects data member. In other words, this data member would be created exclusively for each object, as opposes 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


So the class is more complete right now. Notice that we 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 methods has one argument. That's what I mean by 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 that 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 concerned. So below example will actually works.:

  me = Person()
  me._Person__Name = 'Dody'
  me._Person__LieMyAge()
  me.AboutMe()


Well, off 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 judgement when it is appropriate.


Polymorphism

People would laugh if someone actually talk about Polymorphism without explaining inheritance first. But we can do this in Python due to the way Python handles variables. Python variable does 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. We 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 called show, everything is well and OK. Object type is not important. Virtual methods makes no different 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"


Above example uses the usual algorithm other book use to demonstrate polymorphism. It works simpler in Python. However, you probably miss a subtle thing that the example show. It is that the list can hold three object of different type and call the methods without any typecasting at all. Generic object container, the holy grail of other OO languages. You can't this directly in C++, Object Pascal or even in Java. In Java at least you can use interface as the list type, 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() will raise an exception if the current object referred by x does have show() method. No fear though, 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).


[todo-Inheritance-DG-99/10/2]
[todo-MI-DG-99/10/2]
[todo-Persistency-DG-99/10/2]
--------------BB30EF5D058124AC829991A1--