[ZDP] Python sub-tree draft 3

Dody Gunawinata dody_g@eles.com
Thu, 14 Oct 1999 14:39:57 +1000


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

Ok,

this is draft 3. Things are a lot more complete, although there are some
topics that needs more discussions.

I would 'abandon' this chapter at this draft 3 for a week or so, so that
I can get a 'fresh' look on some of other things before getting it
another round of update. I'm also expecting a further feedback on this
draft 3. Jeff ? :0)

I do like the idea a unit of author and editor. So we got a small team
pairing to produce quality documentation. Small team tends to work
better than a large team or a solo effort.

I split the chapter into two files because my fav text editor, Meganotes
(ironic :), couldn't handle the 7200 words in the chapter.

Ok, for the time being, I'll investigate on the best way to 'teach' the
grande <dtml-var> tags. Yach, I'd like to start some work on the dtml
tags. See how it goes.



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

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 the first argument.

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).

Dictionary

Dictionary is equivalent to associative array in JavaScript, or Perl. It looks like a list although, for the  index
to the content, strings, numbers of python object can be used, instead of just a number. Hence the name dictionary.

Declare an empty dictionary

	dict = {}

Dictionary is a mutable data structure, in other words, you can modify a dictionary, in contrast to tuple, which is immutable.

So to add a tuple.

	dict [2] = 'Johny'
 	dict ['FirstItem'] = 1

Accessing the value is similar

	print dict[2]
	print ['FirstItem']
	#They print 2 and 'FirstItem' respectively

Above statement add two items to the dictionary. You can see that to add a new item, you just declare 'add' a new key and value pair. Unlike in list where you use the *append* method. There is one thing that you must remember. The statement dict [2] does not mean
that the 'Johny' is located after the item [0],[1] like an array or list. No. It just means that we use the number '2' to index an item, which is in this case 'Johny. 

You can also define a dictionary explicitly by using a key:value pair

	dict = {'color':'red','name':'James'}

Next, let play around with some methods that comes with a dictionary.

Get the total number of items in a dictionary. 

	print len (dict)
    #the would display 2

Get all the key in the dictionary (The result is a list)

	print dict.keys()
	#this would display ['color','name']	

Get all the values in the dictionary (The result is a list)

	print dict.values()
	#this would display ['red','James']

Just stop for a while here. Because the method keys() and values() return a list, it means that we can use it in a for loop. (remember that for loop iterates on a list)

In the end, to delete an item in a dictionary, use the 'del' keyword.

	del dict['color']

There are more methods available for a dictionary. Check it out in Python library reference.


Inheritance.

I will not discuss the designed or how you should structure you program around OO design and principle. This topic
will show you how inheritance is done in Python.

	class ClassName (parentClass):
		pass

Assuming that parentClass exist, the statement above is a correct Python inheritance. Off course, there are more things to come.

Let's set up a parent class first, then I will explain some aspect of inheritance in Python.

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

		def tellName (self):
			return self.name

The parent class is trivial for the sake of syntatic simplicity. It has one data member and one methods. The constructore requires an 'name' argument. Simple.
So let's make the 'child' class.

	class Student (Person):
		def __init__ (self, school, name):
			Person.__init__(self,name)
			self.school = school

		def tellSchool(self):
			return self.school

Let's stop and observe what happen. In Python, you 'must' call the Parent constructor manually (There is no rule enforcing this, but the object you will have will not have a correct information). Hence the command, Person.__init__ method calls.

Ok, so let's try the Student class.

	Me = Student ("MI","Felice")

	print Me.tellName()   #displays Felice
	print Me.tellSchool()  #displays MI


So far so good.


There's nothing special about instantiation of derived classes: DerivedClassName() creates a new in-
stance of the class. Method references are resolved as follows: the corresponding class attribute is
searched, descending down the chain of base classes if necessary, and the method reference is valid if this
yields a function object.


Virtual methods

All methods in Python are virtual. So if in a 'child' class, you declare a method with the same name of a method somewhere in the 'parent' class,
when you instantiate the 'child' class and call that method, the method of the 'child' class is used instead of its parents.

So referencing to the previous example, add a method tellName() in Student class:

	class Student(Person):
	[snip other methods]
		def tellName(self):
			return "This is my own name"

	Me = Student("MI","Felice")

	print Me.tellName() #displays 'This is my own name'

So if you this example, instead of displaying "Felice" (which the Person.tellName() method would do), the Student.tellName() is called instead.

Off course, rather than just replace the method, you might to 'extend it'. In order to do that, you might to call the original tellName() method from Person.

	class Student(Person):
	[snip other methods]
		def tellName(self):
			Name = Person.tellName (self)
			return "This is my own name " + Name

	Me = Student ("MI","Felice")

	print Me.tellName() #displays 'This is my own name Felice'

From the example you know that to call the method of the parent class, use this format *parentClassName.methodname(self, arguments)*. The 'arguments' part 
is if the method actually uses arguments.		

An let me tell you one more thing, when Python encounters a method call, it would search the method in the class of which the object is created, if not found, 
Python would search the method in the parent class, and so on (to the 'grandpa' class, 'great grandpa' class, etc). If in the end the method is not found, Python would issue
an error (exception). 


Exceptions

Built in exceptions hierarchy

	Exception
		StandardError
			
			SystemExit, KeyboardInterrupt, ImportError, IOError, EOFError, RuntimeError, 			NameError,
			AttributeError, SyntaxError, TypeError, AssertionError, ValueError, SystemError, 			MemoryError

			LookupError
				IndexError, KeyError
			ArithmeticError
				OverflowError, ZeroDivisionError, FloatingPointError	
			EnvironmentError
				IOError, OSError
			RunTimeError
				NotImplementedError
		
When something wrong, Python would issues an exceptions. The exception would be one of those standard built-in exceptions.

To handle an exception

	try:
		statements
	except ExceptionType var1:
		handling code
	except ExceptionType var2:
		handling code
	except:
		handling code
	

See that you can choose to handle a specific exception, or just use the empty
except statement to handle all exception. 

So, if something wrong happens in the code in the try clause, it would jump to the appropriate except clause, or if the matching exception handler doesn't exist, jumps to the plain vanila 'except:' clause.

There is another keyword in the try..except cause; 'else' keyword. 

	try:
		statements
	except:
		handling code
	else:
		statements

The codes in the else clause would only get executed if there is no exception in the try clause.

You can off course raise an exception deliberately.

	raise exceptionType(args)

You can choose the 'exceptionType' from the standard Python exception, e.g :
	
	try :
		raise IOError ("Oops, can' t read a file")
	except IOError err:
		print err

or you can create your own exception by inheriting from one of the standard Python exception

	class EMyException (Exception):
		pass

	try :
		raise EMyException ("My first exception")
	except IOError err:
		print "The error message is ", err
	except EMyException err:
		print "The error message is ", err
	except:
		print "Exception, but nothing really specific"

	#the whole execution would produce "The error message is My first exception"

Notice that the exception goes straight to the EMyException handler. If you raise
another exception other than IOError and EMyException, the end result would be
"Exception, but nothing really specific"


Multiple inheritance

Mutiple inheritance is supported in Python, just like C++.

This is how you declare it

	class MyClass(FirstParent,SecondParent):
		pass

That's all. This is the minimum valid class that uses multiple inheritance feature.

However, as you might aware of, there are some pitfalls and quirks about MI that you need know.

First, the way Python look for data member and methods. Python goes Depth-first. What does it means ?
So, if if you call a method, Python would the method in the class in which the object is created, then, the first parent class
listed. So in this case, FirstParent. If Python hasn't found it, it would look to the parent of FirstParent, and up and up. If the search
still fails, Python then move to the next parent, SecondParent. And so on.

Let's have a dumb example:

	class Grandpa:
		self.Name = 'Grandpa'

	class Father (Grandpa):
				
	class Mother:
		self.Name = 'Mother'

	class Son (Father, Mother)
		pass

	M = Son()

	print M.Name #this would display 'Grandpa' instead of 'Mother'

You get the idea.



Persistency

Python provides one of the best and easier features to store objects into file. You will see it later, they are very simple and powerful.
This 'save' features is not only for objects however, you can also store strings and numbers.

There are several persistency related libraries available in Python, but the most commonly used is cPickle and Shelve. So let's take a look at them.


cPickle

If you have heard of Pickle, cPikle is its cousin. The differs in implementation. cPickle is implemented in C, therefore it is a lot faster than the Python
implemented Pickle. 


As it is  a module you must import it first 

	import cPickle.

To save an object.

   file = open ("myFile", "w")
   cPickle.dump (a,file)
   cPickle.dump (b,file)
   file.close()

Can you see how simple it is ? You don't have to worry anything else. If your object refers to another object, that *object* would get saved as well, and so on. Just use dump() and you are done. 

To retrieve an object.

   file = open ("myFile", "r")
   cPickle.load (a,file)
   cPickle.load (b,file)
   file.close()

That's it. Now a and b contains the previously saved objects, with the data and all. It's very simple.

However, there is one catch. If you dump() 3 times, you must also load() 3 times. Yes, it means that you must load() all the objects that you have dump().
There is no way that you can just retrieve one item that you need. It's all or nothing approach.

However, the next library, Shelve, would allow you to do just this. 


Shelve

To save an 'object'

  	import Shelve

  	book = Shelve.open ('myFile')
  
	book['Object']     = Items. #Items can be anything
	book['AString']    = "Store this string for me"
	book['ANumber'] = 433253

  	book.close()

To read

	book  = shelve.open ('filename')
  	items = book ['ANumber']
  	book.close()

	print items #this would display 433253

Hey, you said, it looks like a dictionary. Well, it is some sort of dictionary. It's  a persistence dictionary. However, this particular dictionary can only uses string 
as a key to the item, unlike the data structure dictionary that allows you to use tuples, string, etc.

Just as well with cPickle, Shelve do all the hardwork for you, saving referenced objects, datas, methods, etc. You just do this, and it's done. Simple.


In contrast to cPickle, Shelve doesn't require you to load the whole data. You can decide which part to load. Yes, Python retrieve the data from the disk each time
you access specific item, so it's a bit 'slower'. The advantage is obvious, that you can store a large number of data without having to load all of them from memory.



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


Introduction

Let's cut the story short. You read this chapter in order to understand Python. Well, this chapter would do just that. So let's pack up and start. 

A small note

I suggest you to visit to Python.org site to download the standard documentation that comes with regular Python distribution. Python documentation
is unusually thorough for an open project, so it's a good thing. The Python standard library reference is complete and very easy to use.


Getting your Python interpreter

You can get Python interpreter for your operating system from python.org. However, Zope already distributed with
a Python interepter. It is located in <zope-root>/bin directory. Therefore there is no need to download another
Python interpreter, unless you want to have the latest version of the Python interpreter; At this point of writing, the Python intepreter that comes with Zope distribution is current, Python 1.52.


Using your interpreter.

Interactive mode

This is when you invoke the interpreter by just typing the command "python". Of course you would not want to spend a lot of your time programming in this session. Mainly because you can't save what you have written in the interactive mode and well, it's pain in the ass to write a lot of code in it. On the other hand, the interactive session is great for learning the features of the languages and testing library calls. Guido, the father of Python, has written an introductory tutorial for Python using this method. Get it at http://python.org. 

'Compiler' mode
Type source code in text file, save as file with extension .py, type command "python sourcecode.py", and see the result 'mode'.
Once you start to write more and more Python code, this is the only reasonable way of doing it. 

How to use the interpreter with this chapter

In the beginning chapters, I recommend you to use Python interactive session, as it enables you to experiment freely with snippets of codes and get the result
immediately. However, once you reach to the middle of this chapter, things would require slightly more coding, and saving the codes to a file would benefits you greatly.
So in that case, you should use the 'compiler' mode.


Comment

Python has only single line comment, which is marked by '#' sign. Everything between the '#' to the end of line is considered a comment. 

Statement grouping

It's important to note that Python does not use any special symbol for grouping statements. So there is no '{ }' or 'begin end' marker. It uses indentation to group statements. It is a bit unusual but don't worry, over the time you'll appreciate this feature. It forces you to 'pretty print' your source code from the start, which is a Good Thing(tm). You will see this rule in action in the next topic.

There are two ways to indent your code:
1. Uses tab consistently (4 or 8 spaces each tab).
2. Use the good old spaces (4 spaces is the common practice).

Either way it would work, however, never mix the use of the two methods together, because it would confuse the  interpreter and generates error. The use of tabs and spaces for indentation is mutually exclusive. Tab only or spaces only.


Case sensitivity

Python is case sensitive. So 'X' and 'x' are two different things. The most mistakes beginners do in the beginning probably type 'Print' instead of 'print' and wondering
why the command doesn't work.


Variables

-No type declaration. 
-You can declare it anywhere. 
-Variable does not remember its type. 
-You assign value to a variable using '=' sign. 


There is no need to declare the type of your variable. And everything that starts with # is considered as a comment. It's a single line comment.

	#Declare the variables
	stringVariable = "test"
	integerVariable = 24
	floatingVariable = 34.3 

Use the print keyword to print the variable content to the screen. Yes, it's a language feature, not just a function. 

Notice that there is no special symbol or stuff like that in order to print the variable content.

	print stringVariable
	print integerVariable
	print floatingVariable 

To 'chain' several variables in one single print statement, use the comma

	print stringVariable, integerVariable, floatingVariable


You can mix displaying different type of variable in a single print statement. Notice that there is no extra space needed in the string because print would add it automatically if two different type are displayed in the same line. Smart eh?

	print 'My first', stringVariable, "of Python script", integerVariable, floatingVariable 


Using print alone will give you a new line

	print 


You can also do mathematical expression in the print statement.

	print integerVariable, ' * 2 = ', integerVariable * 2 

As you might have noticed from the previous example, every print statement adds a new line. Use a comma (',') to prevent print adding a line break.

	print "this sentence without ",
	print "line breaks" 


Use the '=' to assign value from one variable to another.

	anotherString = stringVariable 


Variable does not 'remember' its previous value's (implicit) type. So you can use one variable over and over again with different type of value. Use it with care.

	stringVariable = floatingVariable 


You can use '' (single quotes) or "" (double quotes) for string literal. To display '' (single quote) in the string, enclose the string within double quotes mark. I prefer double quotes just for its consistency. I use single quotes in the previous examples just to let you know it can be done. After the following example, I will stay true to my double quotes.

	print 'Single quote '
	print " Double quote " 



Data-Structure: List

This is the first of the three built in data structure provided by Python.  List is the most basic and the most similar to the array data structure provided by other traditional languages. Like *variables*, List can contain strings, numbers, and even Python objects (later on this one).

Create empty list

	x = []

To add an element into the list.

	x.append ('First')
	x.append (4)

As you see in the previous example, a list can contains a hetergenous data. The size of the list, thereotically is only limited to the amount of memory in your machine.

To get the number of items in the list, use the Len() function.
	
	print Len (x)
	# this would print 2

List uses zero based index, so the first element would be in index 0, and the last element always in Len(list) - 1 index.

	print x[0] #this would print 'First'

And if you try to access an item that doesn't exist, Python would generate an exception (more on this later), in other words, error.

	print x[2] #this is wrong.
  

To delete an item, use the 'del' keyword. When you delete, the list resizes automatically. So if for example, you delete item at position 3, the item at position
4 would move to position 3, and so on.

	del x[1]
  

You know now on how to create an empty list, how about if you want to initialise the list with some values. 

   p = [None,'This is a string',4]

Above statement should do it. Then again, how about if you want to make a list of the size 40. Well, typing [None,....] for 40 items is tedious, so Python offers you
another way of doing it. It's called 'list multiplication'

	z = [None] * 4
  	
	print z
    #this would print None,None,None,None

Off course you can use another value, or even multiple values sequences to initialise the list.

	z = [None, 'Values', 3] * 2

	print z
	#this would print None,Values,3,None,Values,3


ifs and elses


 if_stmt: "if" expression ":" suite          
 "elif" expression ":" suite)*          
 ["else" ":" suite] 

It supports the usual band of Boolean operators and some. They are 'and, or, not, ==, !=, <> (the same as !=, inequality), >, <, >=, <=' and some more. Let me again emphasise the fact that Python uses indentation for grouping statements. 

	x = 30

	if x > 20 :
   		print "x is less than 20"
	elif x == 20:
   		print "x is equal 20"
   		print "and not less than 20"
	else:
   		print "x is definitely larger than 20"
	
	if x > 40 :
   		print "like this one, larger than 40"
	else :
   		print "still larger then 20, but less or equal 40" 



While loops

 while_stmt: "while" expression ":" suite             
 ["else" ":" suite] 


Let's talk about some keywords exist just for the loop.

break as in C, Java or Object Pascal, breaks out from the current loop. 

continue continues with the next iteration of the loop. 

else clause in below example is optional. It only gets executed when the loop terminates normally through the exhaustion of the list (for loop) or when the condition become false (while loop), but not when the loop is terminated by break statement. So the following code will print 'Get to the end' after the numbers get printed. 

	#Loop 10 times
	x = 0
	while x < 10:
   		print x
   		x = x + 1
	else:
   		print "Get to the end" 


And you know now that the string in the else clause on the following function will not get printed. You might as well remove the else clause. And let me remind you again, Python uses indentation for statement grouping. Notice as well that the else clause are in the same indentation as the while keyword.

	#Loop only 2 times

	x = 0

	while x < 10:
   		if x >= 2:
      		break 
   		print x
   		x = x + 1
	else:
   		print "This ain't getting printed" 


for loops


 for_stmt: "for" target_list "in" expression_list ":" suite           
 ["else" ":" suite] 

There is no concept of loop incrementing a variable from low to high or vice versa in Python. The for loop is designed to iterate on a list. You can define a list and let the for loop iterate over it, or better yet, call range() function to generate the list for you automatically so you can just use it. Just remember that the loop iterates for the total number of element in the list. [100,200] is a list. A loop would iterates twice on this list, just the same as [1,2]. [100,1,200] would result in 3 loops. See that there is no concept of low and high value. As you remember, the for loop also come with the optional else clause. It behaves the same with its while loop counterpart. Let's the loop iterates on your list.

a = ['a','b','c']
for x in a:
   print x #this prints a, b and c 


And check out the list the cool function range() generates for you.

#This is the result of a range function call
print range(3) #prints [0,1,2]
print range(-3,4) #prints [-3,-2,-1,0,1,2,3] 


And use it for your loop pleasure.

#the simple for loop..loop 10 times
for x in range(10):
   print x

#see the flexibility of range function here.
#This loops for 6 times.
for x in range(4,10):
   print x 


Tuples

Tuples is one of the basic data container provided by Python. As like any other data container provided by Python,
Tuples can contains heteregenous data type, such as string and objects.

Tuples look like  a list, however, there is one big difference.

Tuple is an 'immutable' data structure, in a common words means, once you declare a tuple, you cannot add an item or remove
an item from it. That's the meaning of immutable, unchangeable.

List is a 'mutable' data structure, as you can create an empty list and then add or delete the items in it.

This is how you declare a tuple::

	MyTuple = 'FirstElement','SecondElement'

Done.  Notice that it uses comma to separate from one item with another.

As like any other Python data structures, it can be of mixed values

	MyTuple = 'FirstElement', 2, 3, None

You  access the element of the tuple just like a zero based array (the same like a list)

	print MyTuple[0], MyTuple[1]
  
	#MyTuple[0] referes to 'FirstElement' and etc.

The coolest thing is that you can assign the values of the tuple into several variables at once.
 
	First, Second, Third = MyTuple

	print First, Second, Third, 'is the same as', MyTuple[0],MyTuple[1],MyTuple[2]

In python, we call this *tuple unpacking*.


To create empty tuple

	Tuple = ()

To create single tuple

	Tuple = 'Single Tuple',

Notice the comma trailling the first element. If you don't include the trailing comma, the variable declaration
would be treated just as a basic data type (in above example, a string).

Remember that you cannot modify the individual values of a tuple (immutable data structure), so below
examples won't work

MyTuple[0] = 'This is WRONG'


The good old function 

 	funcdef: "def" funcname "(" [parameter_list] ")" ":" suite 
	parameter_list: (defparameter ",")* ("*" identifier [, "**" identifier] | "**" identifier |                
	defparameter [","]) 
	defparameter: parameter ["=" expression] 
	sublist: parameter ("," parameter)* [","] 
	parameter: identifier | "(" sublist ")" 
	funcname: identifier 

Let me remind you again that Python uses indentation to group statements. So like other construct in Python, a function is defined by a def keyword, and statements belong to the function are just indented together.

This is the simplest form of function you can define in Python. Void function without any parameter. Notice the 'documentation string'. It's not required, but it's really a good habit to explain what your function do, and some documentation tools uses this documentation string to document your function automatically. Just like Java with its Javadoc.

	#'void' function without any parameter
	def voidFunction ():
   		"This is known as 'documentation string'"
   		print "voidFunction(): This function returns nothing"

	#This is how you call parameterless function
		voidFunction() 


I only tell you half the truth when I say you can create void function in Python. Your 'void' function actually returns a value, albeit a boring one, None. None in other language is known as null, Null, or Nul in other languages. Notice the capital N.

	#'void' function actually returns 'None' value
	print voidFunction() 


Now, let's create a function that actually returns a real value. As you see, there is no difference between the declaration of the next function with it's 'void' counterparts. The only thing that separates them is that below functions has return statement with value. So the value returned from the function actually depends on what you put on the return statement.


	#This function actually returns something
	def returnFunction():
	"""However, this style with three double quotes
	is recommended, as you see, multiple line
	span"""
   	
		i = "returnFunction(): This string would be returned"
		return i


	#see the result
	print returnFunction() 


The return keyword actually can be used for another purpose, which is it enables you to exit the function immediately. It's useful for some situation. And you don't need to specify a value for the return keyword either. If you don't give a value, it returns None.

	def returnNone():
	"""Documentation is not required, but recommended so
	some external tools can use it to document your function.
	Just like JavaDoc"""
   		return 
		print "returnNone(): This doesn't get printed at all"

	returnNone() #This function does nothing
	print returnNone() #This would print 'None' 


Multiple return values.

You can return more than one values, unlike other programing languages, thanks to tuple.

	def MultipleReturns():
	""" This returns multiple values"""
		A = 'My Name is John'
		B = 'I am'
        C = 20
        D = 'old'

        return A,B,C,D

	Name, Iam, Age, Old = MultipleReturns()
	
	#or just take one

	Name = MultipleReturns()


And of course function in Python can accept arguments. Well, as you might have guessed, there is no type declaration needed either.

Notice the single line function. It's quite common to see Python source code with this kind of style for single statement function. I prefer to indent the single statement because it enables me to read faster.

	#Function with parameter, no type is needed. Notice the
	#single line function
	def parameterFunction (First, Second): print First, ' ', Second

	#Calling it
	parameterFunction ("String arguments", 40)
	parameterFunction (58.43, "it's float") 


As you see above, Python doesn't enforce the type at all. It's up to the programmer to be careful with passing arguments. Python does raise an error if the type is incompatible with the operation in the function, which is not the case with the above function.


This is another trick in Python for function. Default parameter.

	def defaultFunction (MyName, Age = 21, MoneyLeft = 30.25) :
   		print "My name is", MyName
   		print "I am", Age, "years old "
   		print "I have only", MoneyLeft, "in my pocket"

	#Five wicked ways to call the function
	#Just in case you forget, the single print statement just print an new line
	
	print
	defaultFunction ("John")
	print
	defaultFunction ("John",30)
	print
	defaultFunction ("John",30, 300.00)
	print
	defaultFunction ("John", MoneyLeft = 1000.00);
	print
	defaultFunction (Age = 90, MyName = "John") 


Function aliasing

Last but not the least, you can assign your function to another variable. Just like a function pointer (using C terms). Notice that you don't use the () argument mark, because doing so means that you are assigning value from the function instead.

	anotherDefaultFunction = defaultFunction
	anotherDefaultFunction ("John") 



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()


Search path

Python need to know where to find the module that you import. To find out how currently Python look for modules, type the following code :

	import sys

	print sys.path

This would print a list of directory of which Python would look for a module. 

The common setting would have Python to search in the following directories, in the order as they listed here :

	- The current directory where the Python interepreter executable is located.
	- The /lib directory under the directory which Python interpreter is located (e.g. <zope root>/bin/lib)
	- Other directories.

/lib directory contains the standard library that comes with Python, that is why Python can find the module 'string' that you imported before.
Check out this directory and you'll find a file named 'string.py'

So, if you put your own library in these directories, Python would be able to find it. However, you probably would like to put your own
library in a separate directory. Well, off course you can, but you need to 'tell' Python where to find it first. Here's how

	import sys

	sys.path.append ('directorypath')

After this statement, then you can import whatever module you have in that 'directorypath'

By the way, usually sys.path read it's initial value from a variable called PYTHONPATH, but this varies from one installation to another. So if you find 
this variable (or related variable), it's better to change it in *that* variable, so you don't have to type the code above over and over again in your source code. And imagine the correction you have to do when you decided to move the module that you need to another directory.


Package

So we get module, then we have a package. Module is a file containing, most of the time, a set of related objects or functions. The module name is the same as the file name (remember). A package a directory containing one or more modules, again most of the time, that are related to one another. A package name is the same as the directory name. 

When do you use package ? Most of the time when you are having a large Python project, so there is a need to manage a collections of files in a logical and structured format. Also when you have a specialised libraries that  uses a lot of inter-dependent files. There are some more other valid reasons as well. But let's see it first, you might come out with your own reason for using a package.

Let's start with a 'dumb' example.

Say that you have 3 files, with a couple of functions :

	display.py
		maximise()
		minimise()
		close()		
	print.py
		printAll()
	draw.py
		circle()
		square()

then put them in a directory. Let's call it 'ouput' directory.

so you have

	output/display.py
	output/print.py
	output/draw.py

well, know you got the files and the directory. How do you tell Python that you actually got a package ?
Add this file
	output/__init__.py

This can be an empty file, although it can do something useful for the package, such as intialising values. More on this later.

So congratulation, you got yourself a package.

So now, how do you use a package. Well, just similar to the way you use module.

This way you load the display module.

	import output.display

	output.display.minimise()

You still have to qualify the whole package and module name. Not pretty if you use this method a lot.


So, let's import the modules directly, so you just need to qualify the module name

	from output import display

	display.minimise()

Ah, it's better, one less word to type. However you can still go further. Let's import the function individually so you can use it
without any module qualification.

	from output.display import minimise, maximise,close

	minimise()
	maximise()
	close()


But wait you say, can I just import the whole modules in the package ? Something like this

	from output import *

	display.maximise()
	print.printAll()
	draw.circle()

Well, this sometimes works, depends on your operating system. OS like Windows 95/98 doesn't really know about the case of the 
filename and directory. There is no guaranteed way to import a file 'display.py' as display, or Display, or DISPLAY in these OS. Shess, dumb
filesystem you said. Well, there is a way to solve this problem.

That where you can use __init__.py. In this file, define a variable call __all__. So in our case, the it would look like this

	__all__ = ["display","print","draw"]

So have this statement in __init__.py in our output directory. 

So now you can reliably use 'from output import *', and do just like the previous example.


Now that you know a gazzilion way on how to use a package, use them wisely.  


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 a bit difficult for you. But then, you can always just try to read thru this section. See if you can understand part of it. 

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 introduce some confusion, which is often the case with beginners. I'll use the term 'object' instead of 'class instance'. Class is well, a class -the base-, and object is what you call an instance of a class, i.e. that what you create from the class. By this we can say that an
object exists at run-time. There only exists one specific class, but there can exists multiple objects from a class. 

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. Oops, you might wonder what it is. Python enables data members and methods to be added to  a 'live' objects
at run time (and deleted as well). Yes, that's why I use the term "run time object modification". So you can create
an object that does nothing, then add data member and methods to the "empty" object, and use it. This feature is missing
from other "traditional" OO programming language such as C++, Java, Eiffel or Object Pascal. 


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 objects with two child objects:

  	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



--------------BA34B1718AF1CE3FD385766A--