We shall now look at a few more datatypes available in Python, namely,
tuples, dictionaries and sets. Let us start with tuples. 

Tuples
======

We shall learn

 * what are tuples
 * their similarities and dissimilarities with lists
 * why are they needed

Let's get started by defining a tuple. A tuple is defined by enclosing
parentheses around a sequence of items seperated by commas. It is similar to
defining a list except that parentheses are used instead of square brackets.

::

    t = (1, 2.5, "hello", -4, "world", 1.24, 5)
    t

defines a tuple. 

It is not always necessary to use parenthesis around a ``tuple``

::
    a = 1, 2, 3
    a
    (1, 2, 3)

    b = 1, 
    b
    (1,)

The items in the tuple are indexed using numbers and can be accessed by using
their position.

::

    t[3]

prints -4 which is the fourth item of the tuple.

::

    t[1:5:2]

prints the corresponding slice

This is the behaviour similar as to lists. But the difference can be seen
when we try to change an element in the tuple.

::

    t[2] = "Hello"

We can see that, it raises an error saying tuple does not support item
assignment. Tuples are immutable, and cannot be changed after creation.

Then, what's the use of tuples?

We shall understand that soon. But let us look at a simple problem of
swapping values.

``a = 5`` and ``b = 7``. swap the values of a and b

We define the two values

::

    a = 5
    b = 7

    a
    b

Traditionally, we swap them using a temporary variable. 

::

    temp = a
    a = b
    b = temp

    a
    b

The Python way, would be 

::

    a
    b

    a, b = b, a

    a
    b

We see that the values are swapped. This idiom works for different data-types
also.

::

    a = 2.5
    b = "hello"

    a
    b

Moreover this type of behaviour is something that feels natural and you'd
expect to happen.

This is possible because of the immutability of tuples. This process is
called tuple packing and unpacking.

Let us first see what is tuple packing. Type

::

    5,

What we see is a tuple with one element.

::

    5, "hello", 2.5

Now it is a tuple with three elements.

So when we are actually typing two or more elements seperated by commas,
those elements are packed into a tuple.

When you type
::

    a, b = b, a

First the values of b and a are packed into a tuple on the right side and then
unpacked into the variables a and b.

Immutability of tuples ensures that the values are not changed during the
packing and unpacking.

That brings us to the end of our discussion of tuples. Let us now look at
dictionaries. 

Dictionaries
============

A dictionary in general, are designed to be able to look up meanings of
words. Similarly, the Python dictionaries are also designed to look up for a
specific key and retrieve the corresponding value. Dictionaries are data
structures that provide key-value mappings. Dictionaries are similar to lists
except that instead of the values having integer indexes, dictionaries have
keys or strings as indexes. 

We shall now look at creating dictionaries, accessing elements of
dictionaries, checking for presence of elements and iterating over the
elements. 

Let us start by creating an empty dictionary, type the following in
your IPython interpreter.

::

    mt_dict = {}    

Notice that curly braces are used define dictionaries.

Now let us see how to create a non-empty dictionary,

::

    extensions = {'jpg' : 'JPEG Image', 
                  'py' : 'Python script', 
                  'html' : 'Html document', 
                  'pdf' : 'Portable Document Format'}

Notice that each key-value pair is separated by a comma, and each key and
value are separated using a colon.

Here, we defined four entries in the dictionary extensions. The keys are
``jpg``, ``py``, ``html``, and ``pdf``.

Simply type,

::

    extensions

in the interpreter to see the content of the dictionary. Notice that in
dictionaries the order cannot be predicted and you can see that the values
are not in the order that we entered in.

Like in lists, the elements in a dictionary can be accessed using the
index, here the index is the key. Try,

::

    print extensions['jpg']

It printed JPEG Image. And now try,

::

    print extensions['zip']

As expected it gave us an error. Obviously, our dictionary didn't have any
key 'zip', and that's what the error message says.

Well that was about creating dictionaries, now how do we add or delete items.

::

    extensions['cpp'] = 'C++ code'

Adds a new key value pair, ``cpp : C++ code``

We delete items using the ``del`` keyword

::

    del extension['pdf']

Let us check the content of the dictionary now,

::

    extensions

So the changes have been made. Now let us try one more thing,

::

    extensions['cpp'] = 'C++ source code'
    extensions

As you can see, it neither added a new thing nor gave an error, but it
simply replaced the existing value with the new one.

Now let us learn how to check if a particular key is present in the
dictionary. For that we can use ``in``,

::

    'py' in extensions
    'odt' in extensions

It will return ``True`` if the key is found in the dictionary, and
will return ``False`` if key is not present. Note that we can check
only for container-ship of keys in dictionaries and not values.

Now let us see how to retrieve the keys and values. We can use the
method ``keys()`` for getting a list of the keys in a particular
dictionary and the method ``values()`` for getting a list of
values. Let us try them,

::

    extensions.keys()

It returned the ``list`` of keys in the dictionary extensions. And now
the values, 

::

    extensions.values()

It returned the ``list`` of values in the dictionary.

Now let us print the data in the dictionary. We can use ``for`` loop to
iterate.

::

    for each in extensions.keys():
        print each, "-->", extensions[each]


This brings us to the end of our discussion on dictionaries. Let us now look
at sets. 

Sets
====

We shall look at, 

 * sets 
 * operations on sets

Sets are collections of unique elements. ``set`` datastructure in Python
provides an implementation of this. 

Lets look at how to input sets.

::
 
    a_list = [1, 2, 1, 4, 5, 6, 2]
    a = set(a_list)
    a
     
We can see that duplicates are removed and the set contains only unique
elements.

::

    f10 = set([1, 2, 3, 5, 8])
    p10 = set([2, 3, 5, 7])

* f10 is the set of fibonacci numbers from 1 to 10.
* p10 is the set of prime numbers from 1 to 10.

Various operations that we do on sets are possible here also.

The | (pipe) character stands for union

::

    f10 | p10

gives us the union of f10 and p10

The & (ampersand) character stands for intersection.

::

    f10 & p10

gives the intersection

similarly,

::

    f10 - p10

gives all the elements that are in f10 but not in p10

::

    f10 ^ p10

is all the elements in f10 union p10 but not in f10 intersection p10. In
mathematical terms, it gives the symmectric difference.

Sets also support checking of subsets.

::

    b = set([1, 2])
    b < f10

gives a ``True`` since b is a proper subset of f10.

Similarly,
::

    f10 < f10

gives a ``False`` since f10 is not a proper subset.

Where as, 

::

    f10 <= f10

gives ``True`` since every set is a subset of itself.

Sets can be iterated upon just like lists and tuples. 

::

    for i in f10:
        print i,

prints the elements of f10.

The length and containership check on sets is similar as in lists and tuples.

::

    len(f10)

shows 5. And

::
    
    1 in f10
    4 in f10

prints ``True`` and ``False`` respectively

The order in which elements are organised in a set is not to be relied upon.
There is no ordering of elements of a set. Sets do not support indexing and
hence slicing and striding do not make sense, either. 

Here's an example that shows the use of sets. 

Given a list of marks, marks = [20, 23, 22, 23, 20, 21, 23] list all the
duplicates

Duplicates marks are the marks left out when we remove each element of the 
list exactly one time.

::

    marks = [20, 23, 22, 23, 20, 21, 23] 
    marks_set = set(marks)
    for mark in marks_set:
        marks.remove(mark)

    # we are now left with only duplicates in the list marks
    duplicates = set(marks)

This brings us to the end of our discussion on sets. 
.. 
   Local Variables:
   mode: rst
   indent-tabs-mode: nil
   sentence-end-double-space: nil
   fill-column: 77
   End: