basic_python/intro.rst
author Santosh G. Vattam <vattam.santosh@gmail.com>
Tue, 15 Sep 2009 17:02:07 +0530
changeset 52 9748190df418
parent 50 0985027f4c8c
child 65 0f25f22a2725
permissions -rw-r--r--
Completed intro.rst. Review pending.

============
Basic Python
============

This document is intended to be handed out at the end of the workshop. It has
been designed for Engineering students who are Python beginners and have basic
programming skills. The focus is on basic numerics and plotting using Python.

The system requirements:
  * Python - version 2.5.x or newer.
  * IPython
  * Text editor - scite, vim, emacs or whatever you are comfortable with.

Introduction
============

The Python programming language was created by a dutch named Guido van Rossum.
The idea of Python was conceived in December 1989. The name Python has nothing
to do with the reptilian, but its been named after the 70s comedy series 
"Monty Python's Flying Circus", since it happens to be Guido's favourite 
TV series. 

Current stable version of Python is 2.6.x, although Python 3.0 is also the stable
version, it is not backwards compatible with the previous versions and is hence
not entirely popular at the moment. This material will focus on the 2.6.x series.
  
Python is licensed under the Python Software Foundation License (PSF License) 
which is GPL compatible Free Software license (excepting license version 1.6 and 2.0)
It is a no strings attached license, which means the source code is free to modify
and redistribute.

The Python docs define Python as "Python is an interpreted, object-oriented, 
high-level programming language with dynamic semantics." A more detailed summary
can be found at http://www.python.org/doc/essays/blurb.html. Python is a language that
has been designed to help the programmer concentrate on solving the problem at hand
and not worry about the programming language idiosyncrasies.

Python is a highly cross platform compatible language on account of it being an 
interpreted language. It is highly scalable and hence has been adapted to run on 
the Nokia 60 series phones. Python has been designed to be readable and easy to use

**Resources available for reference**

* Web: http://www.python.org
* Doc: http://www.python.org/doc
* Free Tutorials:
    * Official Python Tutorial: http://docs.python.org/tut/tut.html
    * Byte of Python: http://www.byteofpython.info/
    * Dive into Python: http://diveintopython.org/

**Advantages of Python - Why Python??**

* Python has been designed for readability and ease of use. Its been designed in 
  such a fashion that it imposes readability on the programmer. Python does away
  with the braces and the semicolons and instead implements code blocks based on 
  indentation, thus enhancing readability. 

* Python is a high level, interpreted, modular and object oriented language.
  Python performs memory management on its own, thus the programmer need not bother
  about allocating and deallocating memory to variables. Python provides extensibility
  by providing modules which can be easily imported similar to headers in C and 
  packages in Java. Python is object oriented and hence provides all the object oriented
  characteristics such as inheritance, encapsulation and polymorphism.

* Python offers a highly powerful interactive programming interface in the form
  of the 'Interactive Interpreter' which will be discussed in more detail in the 
  following sections.

* Python provides a rich standard library and an extensive set of modules. The 
  power of Python modules can be seen in this slightly exaggerated cartoon
  http://xkcd.com/353/

* Python interfaces well with most other programming languages such as C, C++ 
  and FORTRAN.

Although, Python has one setback. Python is not fast as some of the compiled 
languages like C or C++. Yet, the amount of flexibility and power more than make
up for this setback.


The Python Interpreter
======================

The Interactive Interpreter
~~~~~~~~~~~~~~~~~~~~~~~~~~~

Typing *python* at the shell prompt on any standard Unix/Gnu-Linux system and
hitting the enter key fires up the Python 'Interactive Interpreter'. The Python
interpreter is one of the most integral features of Python. The prompt obtained
when the interactive interpreter is similar to what is shown below. The exact
appearance might differ based on the version of Python being used. The ``>>>``
thing shown is the python prompt. When something is typed at the prompt and the
enter key is hit, the python interpreter interprets the command entered and
performs the appropriate action. All the examples presented in this document are
to be tried hands on, on the interactive interpreter.

::

  Python 2.5.2 (r252:60911, Oct  5 2008, 19:24:49) 
  [GCC 4.3.2] on linux2
  Type "help", "copyright", "credits" or "license" for more information.
  >>> 

Lets try with an example, type ``print 'Hello, World!'`` at the prompt and hit
the enter key. 

::

  >>> print 'Hello, World!'
  Hello, World!

This example was quite straight forward, and thus we have written our first
line of Python code. Now let us try typing something arbitrary at the prompt.
For example: 

::
  
  >>> arbit word
    File "<stdin>", line 1
      arbit word
              ^
  SyntaxError: invalid syntax
  >>>
    
The interpreter gave an error message saying that 'arbit word' was invalid
syntax which is valid. The interpreter is an amazing tool when learning to
program in Python. The interpreter provides a help function that provides the
necessary documentation regarding all Python syntax, constructs, modules and
objects. Typing *help()* at the prompt gives the following output:

::
  
  >>> help()
  
  Welcome to Python 2.5!  This is the online help utility.
  
  If this is your first time using Python, you should definitely check out
  the tutorial on the Internet at http://www.python.org/doc/tut/.
  
  Enter the name of any module, keyword, or topic to get help on writing
  Python programs and using Python modules.  To quit this help utility and
  return to the interpreter, just type "quit".
  
  To get a list of available modules, keywords, or topics, type "modules",
  "keywords", or "topics".  Each module also comes with a one-line summary
  of what it does; to list the modules whose summaries contain a given word
  such as "spam", type "modules spam".
  
  help> 
  

As mentioned in the output, entering the name of any module, keyword or topic
will provide the documentation and help regarding the same through the online
help utility. Pressing *Ctrl+d* exits the help prompt and returns to the
python prompt. 

Let us now try a few examples at the python interpreter. 

Eg 1:
::
  
  >>> print 'Hello, python!'
  Hello, python!
  >>>
  
Eg 2:
::
  
  >>> print 4321*567890
  2453852690
  >>> 
  
Eg 3:
::
  
  >>> 4321*567890
  2453852690L
  >>>

::
  
  Note: Notice the 'L' at the end of the output. The 'L' signifies that the
  output of the operation is of type *long*. It was absent in the previous
  example because we used the print statement. This is because *print* formats
  the output before displaying.
  
Eg 4:
::
  
  >>> big = 12345678901234567890 ** 3
  >>> print big
  1881676372353657772490265749424677022198701224860897069000
  >>> 

::
  
  This example is to show that unlike in C or C++ there is no limit on the
  value of an integer.

Try this on the interactive interpreter:
``import this``

*Hint: The output gives an idea of Power of Python*

*ipython* - An enhanced interactive Python interpreter
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The power and the importance of the interactive interpreter was the highlight
of the previous section. This section provides insight into the enhanced
interpreter with more advanced set of features called **ipython**. Entering
*ipython* at the shell prompt fires up the interactive interpreter. 

::
  
  $ ipython
  Python 2.5.2 (r252:60911, Oct  5 2008, 19:24:49) 
  Type "copyright", "credits" or "license" for more information.
  
  IPython 0.8.4 -- An enhanced Interactive Python.
  ?         -> Introduction and overview of IPython's features.
  %quickref -> Quick reference.
  help      -> Python's own help system.
  object?   -> Details about 'object'. ?object also works, ?? prints more.
  
  In [1]: 
  
This is the output obtained upon firing ipython. The exact appearance may
change based on the Python version installed. The following are some of the
various features provided by **ipython**:
  
    Suggestions - ipython provides suggestions of the possible methods and
    operations available for the given python object.

Eg 5:
  
::
  
  In [4]: a = 6
  
  In [5]: a.
  a.__abs__           a.__divmod__        a.__index__         a.__neg__          a.__rand__          a.__rmod__          a.__rxor__
  a.__add__           a.__doc__           a.__init__          a.__new__          a.__rdiv__          a.__rmul__          a.__setattr__
  a.__and__           a.__float__         a.__int__           a.__nonzero__      a.__rdivmod__       a.__ror__           a.__str__
  a.__class__         a.__floordiv__      a.__invert__        a.__oct__          a.__reduce__        a.__rpow__          a.__sub__
  a.__cmp__           a.__getattribute__  a.__long__          a.__or__           a.__reduce_ex__     a.__rrshift__       a.__truediv__
  a.__coerce__        a.__getnewargs__    a.__lshift__        a.__pos__          a.__repr__          a.__rshift__        a.__xor__
  a.__delattr__       a.__hash__          a.__mod__           a.__pow__          a.__rfloordiv__     a.__rsub__          
  a.__div__           a.__hex__           a.__mul__           a.__radd__         a.__rlshift__       a.__rtruediv__      

In this example, we initialized 'a' (a variable - a concept that will be
discussed in the subsequent sections.) to 6. In the next line when the *tab* key
is pressed after typing '*a.*' ipython displays the set of all possible methods
that are applicable on the object 'a' (an integer in this context). Ipython
provides many such datatype specific features which will be presented in the
further sections as and when the datatypes are introduced.

Editing and running a python file
=================================

The previous sections focused on the use of the interpreter to run python code.
While the interpeter is an excellent tool to test simple solutions and
experiment with small code snippets, its main disadvantage is that everything
written in the interpreter is lost once its quit. Most of the times a program is 
used by people other than the author. So the programs have to be available in 
some form suitable for distribution, and hence they are written in files. This 
section will focus on editing and running python files. Start by opening a text 
editor ( it is recommended you choose one from the list at the top of this page ).
In the editor type down python code and save the file with an extension **.py** 
(python files have an extension of .py). Once done with the editing, save the 
file and exit the editor. 

Let us look at a simple example of calculating the gcd of 2 numbers using Python:

**Creating the first python script(file)**
::

  $ emacs gcd.py
    def gcd(x,y):
      if x % y == 0:
        return y
      return gcd(y, x%y)
  
    print gcd(72, 92)

To run the script, open the shell prompt, navigate to the directory that 
contains the python file and run ``python <filename.py>`` at the prompt ( in this 
case filename is gcd.py )

**Running the python script**
::
  
  $ python gcd.py
  4
  $ 

Another method to run a python script would be to include the line

``#! /usr/bin/python``

at the beginning of the python file and then make the file executable by 

$ chmod a+x *filename.py*

Once this is done, the script can be run as a standalone program as follows:

$ ./*filename.py*

Basic Datatypes and operators in Python
=======================================

Python provides the following set of basic datatypes.

  * Numbers: int, float, long, complex
  * Strings
  * Boolean

Numbers
~~~~~~~

Numbers were introduced in the examples presented in the interactive interpreter
section. Numbers include types as mentioned earlier viz., int (integers), float 
(floating point numbers), long (large integers), complex (complex numbers with 
real and imaginary parts). Python is not a strongly typed language, which means 
the type of a variable need not mentioned during its initialization. Let us look
at a few examples.

Eg 6:
::
  
  >>> a = 1 #here a is an integer variable

Eg 7:
::

  >>> lng = 122333444455555666666777777788888888999999999 #here lng is a variable of type long
  >>> lng
  122333444455555666666777777788888888999999999L #notice the trailing 'L'
  >>> print lng
  122333444455555666666777777788888888999999999 #notice the absence of the trailing 'L'
  >>> lng+1
  122333444455555666666777777788888889000000000L


Long numbers are the same as integers in almost all aspects. They can be used in
operations just like integers and along with integers without any distinction.
The only distinction comes during type checking (which is not a healthy practice).
Long numbers are tucked with a trailing 'L' just to signify that they are long.
Notice that in the example just lng at the prompt displays the value of the variable
with the 'L' whereas ``print lng`` displays without the 'L'. This is because print 
formats the output before printing. Also in the example, notice that adding an 
integer to a long does not give any errors and the result is as expected. So for
all practical purposes longs can be treated as ints.

Eg 8:
::

  >>> fl = 3.14159 #fl is a float variable
  >>> e = 1.234e-4 #e is also a float variable, specified in the exponential form
  >>> a = 1
  >>> b = 2
  >>> a/b #integer division
  0
  >>> a/fl #floating point division
  0.31831015504887655
  >>> e/fl
  3.9279473133031364e-05


Floating point numbers, simply called floats are real numbers with a decimal point.
The example above shows the initialization of a float variable. Shown also in this
example is the difference between integer division and floating point division.
'a' and 'b' here are integer variables and hence the division gives 0 as the quotient.
When either of the operands is a float, the operation is a floating point division,
and the result is also a float as illustrated.

Eg 9:
::

  >>> cplx = 3 + 4j #cplx is a complex variable
  >>> cplx
  (3+4j)
  >>> print cplx.real #prints the real part of the complex number
  3.0
  >>> print cplx.imag #prints the imaginary part of the complex number
  4.0
  >>> print cplx*fl  #multiplies the real and imag parts of the complex number with the multiplier
  (9.42477+12.56636j)
  >>> abs(cplx) #returns the absolute value of the complex number
  5.0

Python provides a datatype for complex numbers. Complex numbers are initialized 
as shown in the example above. The *real* and *imag* operators return the real and
imaginary parts of the complex number as shown. The *abs()* returns the absolute
value of the complex number.

Variables
~~~~~~~~~

Variables are just names that represent a value. Variables have already been 
introduced in the various examples from the previous sections. Certain rules about
using variables:

  * Variables have to be initialized or assigned a value before being used.
  * Variable names can consist of letters, digits and underscores(_).
  * Variable names cannot begin with digits, but can contain digits in them.

In reference to the previous section examples, 'a', 'b', 'lng', 'fl', 'e' and 'cplx'
are all variables of various datatypes.

::
  
  Note: Python is not a strongly typed language and hence an integer variable can at a
  later stage be used as a float variable as well.

Strings
~~~~~~~

Strings are one of the essential data structures of any programming language.
The ``print "Hello, World!"`` program was introduced in the earlier section, and
the *"Hello, World!"* in the print statement is a string. A string is basically 
a set of characters. Strings can be represented in various ways shown below:

::

  s = 'this is a string'              # a string variable can be represented using single quotes
  s = 'This one has "quotes" inside!' # The string can have quotes inside it as shown
  s = "I have 'single-quotes' inside!"
  l = "A string spanning many lines\
  one more line\
  yet another"                        # a string can span more than a single line.
  t = """A triple quoted string does  # another way of representing multiline strings.
  not need to be escaped at the end and
  "can have nested quotes" etc."""

Try the following on the interpreter:
``s = 'this is a string with 'quotes' of similar kind'``

**Exercise: How to use single quotes within single quotes in a string as shown 
in the above example without getting an error?**

String operations
-----------------

A few basic string operations are presented here. 

**String concatenation**
String concatenation is done by simple addition of two strings.

::

  >>> x = 'Hello'
  >>> y = ' Python'
  >>> print x+y
  Hello Python

*Try this yourself:*

::
  
  >>> somenum = 13
  >>> print x+somenum

The problem with the above example is that here a string variable and an integer
variable are trying to be concantenated. To obtain the desired result from the 
above example the str(), repr() and the `` can be used.

**str()** simply converts a value to a string in a reasonable form.
**repr()** creates a string that is a representation of the value.

The difference can be seen in the example shown below:

::
  
  >>> str(1000000000000000000000000000000000000000000000000L)
  '1000000000000000000000000000000000000000000000000'
  >>> repr(1000000000000000000000000000000000000000000000000L)
  '1000000000000000000000000000000000000000000000000L'

It can be observed that the 'L' in the long value shown was omitted by str(), 
whereas repr() converted that into a string too. An alternative way of using 
repr(value) is ```value```. 

A few more examples:
::
  
  >>> x = "Let's go \nto Pycon"
  >>> print x
  Let's go 
  to Pycon

In the above example, notice that the '\n'(newline) character is formatted and 
the string is printed on two lines. The strings discussed until now were normal 
strings. Other than these there are two other types of strings namely, raw strings
and unicode strings.

**Raw strings** are strings which are unformatted, that is the backslashes(\) are 
not parsed and are left as it is in the string. Raw strings are represented with
an 'r' at the start of a string. 
Let us look at an example

::
  
  >>> x = r"Let's go \nto Pycon"
  >>> print x
  Let's go \nto Pycon

Note: The '\n' is not being parsed into a new line and is left as it is.

*Try this yourself:*

::
  
  >>> x = r"Let's go to Pycon\"

**Unicode strings** are strings where the characters are Unicode characters as 
opposed to ASCII characters. Unicode strings are represented with a 'u' at the 
start of the string.
Let us look at an example:

::
  
  >>> x = u"Let's go to Pycon!"
  >>> print x
  Let's go to Pycon!

Boolean
~~~~~~~

Python also provides special Boolean datatype. A boolean variable can assume a 
value of either *True* or *False* (Note the capitalizations). 

Let us look at examples:

::

  >>> t = True
  >>> f = not t
  >>> print f
  False
  >>> f or t
  True
  >>> f and t
  False

The **while** loop
~~~~~~~~~~~~~~~~~~

The Python **while** loop is similar to the C/C++ while loop. The syntax is as
follows:

::

  statement 0
  while condition:
    statement 1 #while block
    statement 2 #while block
  statement 3 #outside the while block.

Let us look at an example:

::

    >>> x = 1  
    >>> while x <= 5:
    ...   print x
    ...   x += 1
    ... 
    1
    2
    3
    4
    5

The **if** conditional
~~~~~~~~~~~~~~~~~~~~~~

The Python **if** block provides the conditional execution of statements. 
If the condition evaluates as true the block of statements defined under the if 
block are executed.

If the first block is not executed on account of the condition not being satisfied,
the set of statements in the **else** block are executed.

The **elif** block provides the functionality of evaluation of multiple conditions
as shown in the example.

The syntax is as follows:

::

  if condition :
      statement_1
      statement_2

  elif condition:
      statement_3
      statement_4
  else:
      statement_5
      statement_6

Let us look at an example:

::
  
   >>> n = raw_input("Input a number:")
   >>> if n < 0:
         print n," is negative"
         elif n > 0:
         print n," is positive"
         else:
         print n, " is 0"

**raw_input()**
~~~~~~~~~~~~~~~

In the previous example we saw the call to the raw_input() subroutine. 
The **raw_input()** method is used to take user inputs through the console.
Unlike **input()** which assumes the data entered by the user as a standard python
expression, **raw_input()** treats all the input data as raw data and converts
everything into a string. To illustrate this let us look at an example.

::

  >>> input("Enter a number thats a palindrome:")
  Enter a number thats a palindrome:121
  121

  >>> input("Enter your name:")
  Enter your name:PythonFreak
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "<string>", line 1, in <module>
  NameError: name 'PythonFreak' is not defined

As shown above the **input()** assumes that the data entered is a valid Python
expression. In the first call it prompts for an integer input and when entered
it accepts the integer as an integer, whereas in the second call, when the string
is entered without the quotes, **input()** assumes that the entered data is a valid
Python expression and hence it raises and exception saying PythonFreak is not 
defined.

::

  >>> input("Enter your name:")
  Enter your name:'PythonFreak'
  'PythonFreak'
  >>> 

Here the name is accepted because its entered as a string (within quotes). But
its unreasonable to go on using quotes each time a string is entered. Hence the
alternative is to use **raw_input()**.

Let us now look at how **raw_input()** operates with an example.

::

  >>> raw_input("Enter your name:")
  Enter your name:PythonFreak
  'PythonFreak'

Observe that the **raw_input()** is converting it into a string all by itself.

::

  >>> pal = raw_input("Enter a number thats a palindrome:")
  Enter a number thats a palindrome:121
  '121'

Observe that **raw_input()** is converting the integer 121 also to a string as 
'121'. Let us look at another example:

::
  
  >>> pal = raw_input("Enter a number thats a palindrome:")
  Enter a number thats a palindrome:121
  >>> pal + 2
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
  TypeError: cannot concatenate 'str' and 'int' objects
  >>> pal
  '121'

Observe here that the variable *pal* is a string and hence integer operations
cannot be performed on it. Hence the exception is raised.

**int()** method
~~~~~~~~~~~~~~~~

Generally for computing purposes, the data used is not strings or raw data but 
on integers, floats and similar mathematical data structures. The data obtained
from **raw_input()** is raw data in the form of strings. In order to obtain integers
from strings we use the method **int()**. 

Let us look at an example.

::

  >>> intpal = int(pal)
  >>> intpal
  121

In the previous example it was observed that *pal* was a string variable. Here
using the **int()** method the string *pal* was converted to an integer variable.

*Try This Yourself:*

::

  >>> stringvar = raw_input("Enter a name:")
  Enter a name:Guido Van Rossum
  >>> stringvar
  'Guido Van Rossum'
  >>> numvar = int(stringvar)


Functions in Python: **def**
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

*Functions* allow us to enclose a set of statements and call the function again
and again instead of repeating the group of statements everytime. Functions also
allow us to isolate a piece of code from all the other code and provides the
convenience of not polluting the global variables.

*Function* in python is defined with the keyword **def** followed by the name
of the function, in turn followed by a pair of parenthesis which encloses the
list of parameters to the function. The definition line ends with a ':'. The
definition line is followed by the body of the function intended by one block.
The *Function* must return a value::

  def factorial(n):
    fact = 1
    for i in range(2, n):
      fact *= i

    return fact

The code snippet above defines a function with the name factorial, takes the
number for which the factorial must be computed, computes the factorial and
returns the value.

A *Function* once defined can be used or called anywhere else in the program. We
call a fucntion with its name followed by a pair of parenthesis which encloses
the arguments to the function.

The value that function returns can be assigned to a variable. Let's call the
above function and store the factorial in a variable::

  fact5 = factorial(5)

The value of fact5 will now be 120, which is the factorial of 5. Note that we
passed 5 as the argument to the function.

It may be necessary to document what the function does, for each of the function
to help the person who reads our code to understand it better. In order to do
this Python allows the first line of the function body to be a string. This
string is called as *Documentation String* or *docstring*. *docstrings* prove
to be very handy since there are number of tools which can pull out all the
docstrings from Python functions and generate the documentation automatically
from it. *docstrings* for functions can be written as follows::

  def factorial(n):
    'Returns the factorial for the number n.'
    fact = 1
    for i in range(2, n):
      fact *= i

    return fact

An important point to note at this point is that, a function can return any
Python value or a Python object, which also includes a *Tuple*. A *Tuple* is
just a collection of values and those values themselves can be of any other
valid Python datatypes, including *Lists*, *Tuples*, *Dictionaries* among other
things. So effectively, if a function can return a tuple, it can return any
number of values through a tuple

Let us write a small function to swap two values::

  def swap(a, b):
    return b, a

  c, d = swap(a, b)

Function scope
---------------
The variables used inside the function are confined to the function's scope
and doesn't pollute the variables of the same name outside the scope of the
function. Also the arguments passed to the function are passed by-value if
it is of basic Python data type::

  def cant_change(n):
    n = 10

  n = 5
  cant_change(n)

Upon running this code, what do you think would have happened to value of n
which was assigned 5 before the function call? If you have already tried out
that snippet on the interpreter you already know that the value of n is not
changed. This is true of any immutable types of Python like *Numbers*, *Strings*
and *Tuples*. But when you pass mutable objects like *Lists* and *Dictionaries*
the values are manipulated even outside the function::

  >>> def can_change(n):
  ...   n[1] = James
  ...

  >>> name = ['Mr.', 'Steve', 'Gosling']
  >>> can_change(name)
  >>> name
  ['Mr.', 'James', 'Gosling']

If nothing is returned by the function explicitly, Python takes care to return
None when the funnction is called.

Default Arguments
-----------------

There may be situations where we need to allow the functions to take the
arguments optionally. Python allows us to define function this way by providing
a facility called *Default Arguments*. For example, we need to write a function
that returns a list of fibonacci numbers. Since our function cannot generate an
infinite list of fibonacci numbers, we need to specify the number of elements
that the fibonacci sequence must contain. Suppose, additionally, we want to the
function to return 10 numbers in the sequence if no option is specified we can
define the function as follows::

  def fib(n=10):
    fib_list = [0, 1]
    for i in range(n - 2):
      next = fib_list[-2] + fib_list[-1]
      fib_list.append(next)
    return fib_list

When we call this function, we can optionally specify the value for the
parameter n, during the call as an argument. Calling with no argument and
argument with n=5 returns the following fibonacci sequences::

  fib()
  [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
  fib(5)
  [0, 1, 1, 2, 3]

Keyword Arguments
-----------------

When a function takes a large number of arguments, it may be difficult to
remember the order of the parameters in the function definition or it may
be necessary to pass values to only certain parameters since others take
the default value. In either of these cases, Python provides the facility
of passing arguments by specifying the name of the parameter as defined in
the function definition. This is known as *Keyword Arguments*. 

In a function call, *Keyword arguments* can be used for each argument, in the
following fashion::

  argument_name=argument_value
  Also denoted as: keyword=argument

  def wish(name='World', greetings='Hello'):
    print "%s, %s!" % (greetings, name)

This function can be called in one of the following ways. It is important to
note that no restriction is imposed in the order in which *Keyword arguments*
can be specified. Also note, that we have combined *Keyword arguments* with
*Default arguments* in this example, however it is not necessary::

  wish(name='Guido', greetings='Hey')
  wish(greetings='Hey', name='Guido')

Calling functions by specifying arguments in the order of parameters specified
in the function definition is called as *Positional arguments*, as opposed to
*Keyword arguments*. It is possible to use both *Positional arguments* and 
*Keyword arguments* in a single function call. But Python doesn't allow us to
bungle up both of them. The arguments to the function, in the call, must always
start with *Positional arguments* which is in turn followed by *Keyword
arguments*::

  def my_func(x, y, z, u, v, w):
    # initialize variables.
    ...
    # do some stuff 
    ...
    # return the value

It is valid to call the above functions in the following ways::

  my_func(10, 20, 30, u=1.0, v=2.0, w=3.0)
  my_func(10, 20, 30, 1.0, 2.0, w=3.0)
  my_func(10, 20, z=30, u=1.0, v=2.0, w=3.0)
  my_func(x=10, y=20, z=30, u=1.0, v=2.0, w=3.0)

Following lists some of the invalid calls::

  my_func(10, 20, z=30, 1.0, 2.0, 3.0)
  my_func(x=10, 20, z=30, 1.0, 2.0, 3.0)
  my_func(x=10, y=20, z=30, u=1.0, v=2.0, 3.0)

Parameter Packing and Unpacking
-------------------------------

The positional arguments passed to a function can be collected in a tuple
parameter and keyword arguments can be collected in a dictionary. Since keyword
arguments must always be the last set of arguments passed to a function, the
keyword dictionary parameter must be the last parameter. The function definition
must include a list explicit parameters, followed by tuple paramter collecting
parameter, whose name is preceded by a *****, for collecting positional
parameters, in turn followed by the dictionary collecting parameter, whose name
is preceded by a ****** ::

  def print_report(title, *args, **name):
    """Structure of *args*
    (age, email-id)
    Structure of *name*
    {
        'first': First Name
        'middle': Middle Name
        'last': Last Name
    }
    """
    
    print "Title: %s" % (title)
    print "Full name: %(first)s %(middle)s %(last)s" % name
    print "Age: %d\nEmail-ID: %s" % args

The above function can be called as. Note, the order of keyword parameters can
be interchanged::

  >>> print_report('Employee Report', 29, 'johny@example.com', first='Johny',
                   last='Charles', middle='Douglas')
  Title: Employee Report
  Full name: Johny Douglas Charles
  Age: 29
  Email-ID: johny@example.com

The reverse of this can also be achieved by using a very identical syntax while
calling the function. A tuple or a dictionary can be passed as arguments in
place of a list of *Positional arguments* or *Keyword arguments* respectively
using ***** or ****** ::

  def print_report(title, age, email, first, middle, last):
    print "Title: %s" % (title)
    print "Full name: %s %s %s" % (first, middle, last)
    print "Age: %d\nEmail-ID: %s" % (age, email)

  >>> args = (29, 'johny@example.com')
  >>> name = {
          'first': 'Johny',
          'middle': 'Charles',
          'last': 'Douglas'
          }
  >>> print_report('Employee Report', *args, **name)
  Title: Employee Report
  Full name: Johny Charles Douglas
  Age: 29
  Email-ID: johny@example.com

Nested Functions and Scopes
---------------------------

Python allows nesting one function inside another. This style of programming
turns out to be extremely flexible and powerful features when we use *Python
decorators*. We will not talk about decorators is beyond the scope of this
course. If you are interested in knowing more about *decorator programming* in
Python you are suggested to read:

| http://avinashv.net/2008/04/python-decorators-syntactic-sugar/
| http://personalpages.tds.net/~kent37/kk/00001.html

However, the following is an example for nested functions in Python::

  def outer():
    print "Outer..."
    def inner():
      print "Inner..."
    print "Outer..."
    inner()
  
  >>> outer()