Introduction to Python
The metabolic modelling software we will be using, ScrumPy, is written in Python. Python is a high-level, object-oriented, interpreted programming language, it has a large standard library, and supports multiple programming paradigms. It is also syntactically clear and easy to learn. This is a very brief introduction to some of the basic features of the language, for a more complete introduction to the topic, see Lutz & Ascher, "Learning Python" O'Reilly Media inc. (Edition 2 or greater). A good source of Python documentation can be found here.
Note
The purpose of these exercises is not to attempt to turn you into a programmer, merely to be able use some features and concepts associated with the Python language. If we liken Python to a complete engineering workshop, you are learning to replace the spark-plugs and check the tyre pressures on your car, not design and build a new car from scratch !
Getting started
We will be using Python from the ScrumPy environment. To start a new ScrumPy session open a terminal and type "ScrumPy":
user@machine:~$ ScrumPy &
which will launch the ScrumPy window.
Data types
Any data we may wish to store or manipulate is associated with a specific type. The ways in which the data may be manipulated depends on the type. For example whole numbers are associated with the "int" type and can simply be added together:
2 + 1 = 3
Likewise, sequences of characters are associated with the "string" type, and they too, can be added together:
"Apple" + "Pie" = "ApplePie"
However a string and an integer, cannot be meaningfully added together, so:
"Apple" + 2 = !! ERROR !!
A given data type can be as simple as a single digit, or as complex as a suite of genome databases. Python comes with a number of relativley simple (but still extrememely useful) data-types, and ScrumPy extends this by providing additional data-types particularly useful for metabolic modelling and related activities.
We will start here by examining some of the common built-in types.
Numerical types
The numerical types we will be dealing with are integers, int, and floating-point numbers, float. Integers are written as a sequence of digits. Floats are written as digits with a decimal point in the sequence, and an optional exponent (e or E).
The type of a given data object can be checked using the built-in function type().
Floats and integers can be interconverted using the constructors int() or float().
The common mathematical operators (+,-,/,*) work as expected, note that x**y means xy.
Boolean
Booleans are a subtype of integers. A boolean type is either True or False, and can be very useful when writing conditional statements, i.e. if something is True, do something. Also, the integer 0 is False.
Strings
Strings are collections of characters. Characters in a string can be accessed by indexing, and membership of a subset of characters in a string can be evaluated.
Lists (and tuples)
Lists and tuples are collections of items in which are stored in a specific order and each item is associated with (indexed by) an integer. The main difference between the two is that tuples are immutable - once a tuple is created it cannot be changed, whereas lists can. For these exercises we will mainly use lists. An empty list can be created by assigning a pair of closed square brackets to a variable.
1 >>> empty_list=[]
Items can be appended to a list by using the append() method.
A list can also be created and populated in one go.
Items can be removed from a list using the remove() method.
As with strings, indexing can be used to copy a subset of a list, keep in mind that the indices of items in lists (like characters in strings) are numbered from 0. Membership of an item in a list can be evaluated as described for strings.
The index of a known item can be retrieved using the index() method.
Dictionaries
In other programming languages dictionaries are sometimes called "associative arrays". Unlike lists, dictionaries store collections of items that are ordered by keys, not indices. There is no specific order of the items in a dictionary. The keys of a dictionary must be unique (for a given dictionary) and be hashable, for now this means that any object that is not a list can be used as a key. Here are some examples of dictionaries in action:
1 >>> dict_1 = {'alfa':1,'beta':2} #create a dictionary
2 >>> keys = ['alfa','beta']
3 >>> vals = [1,2]
4 >>> dict_1 = dict(zip(keys,vals)) #create a dictionary from two lists
5 >>> dict_1
6 {'alfa':1,'beta':2}
7 >>> dict_1['alfa'] #access value '1' by key 'alfa'
8 1
9 >>> dict_1.has_key('beta') #check that dict_1 has key 'beta'
10 True
11 >>> dict_1.keys() #print keys of dict_1
12 ['alfa','beta']
13 >>> dict_1.values() #print values
14 [1,2]
Modules and functions
Python can be used in interactive mode (as we have seen above), as well as in batch mode. A pice of Python code can be saved in a text file with the file extension .py (in ScrumPy the most convenient option is the build-in IDLE text editor, accessed from the tool-bar under File> New window). The module can be run in the ScrumPy shell by importing the file. Assuming we have saved a file names some_py.py in the current directory, this is how it works:
user@machine:~$ ls *.py #in terminal, list all .py files some_py.py user@machine:~$ more some_py.py #Python 'Hello world!' program print 'Hello world!'
It is usually more convenient to structure the stored code into functions that can be executed from the imported module. Functions are defined using the key-word def.
user@machine:~$ more some_py_func.py #Python 'Hello world!' program, accessed by function def print_hello(): print 'Hello world!'
Functions often require arguments that the user is supposed to provide.
user@machine:~$ more some_py_func.py #print user-specified string def print_string(string): print string
In many applications functions return objects to the user.
user@machine:~$ more some_py_func.py #return list of characters in user-specified string def print_string(string): return list(string)
Objects
As mentioned, Python supports multiple programming paradigms, one of those being object-orientation. Object-orientation allows collection of data into objects, or class instances. The data collected in objectes is referred to as fields or attributes, objects also store functions that usually performs actions on the attributes. Object-specific functions are called methods. Here is a small example of class definition, initialisation, and usage of a class that stores a list and a dictionary.
user@machine:~$ more StringDL.py class StringDnLs: def __init__(self): self.str_list=[] self.str_dict={} def add2List(self,item): str_list.append(item) def add2Dict(self,key,val): str_dict[key]=val
1 >>> import StringDL #import module
2 >>> strdn = StringDL.StringDnLs() #create instance of class StringDnLs
3 >>> strdn.str_list #print StringDnLs field str_list
4 [] # which is empty
5 >>> strdn.addList('a string') # add a string to str_list using method addList(item)
6 >>> strdn.str_list
7 ['a string']
Loops, conditionals, assignment, evaluation, and others
Some of the conventions for Python syntax we have already seen. Useful built-in functions include len(), which returns the length of an object,
and dir(), which returns a list of methods and attributes of an object.
The for loop is used to iterate over an iterable object, e.g. a list. Depending on how the loop is formulated the loop variable will either be an item in the iterable object or an index.
while loops iterate until a condition is fulfilled.
This implies that if the condition i<len(a_list) is never fulfilled the loop continues indefinitely, which it will.
Loops can be combined with conditional statements, where a block of code is executed if a statement is true, else another block is executed. The else block is optional, but must be the last option and no statement may follow on the same line.
If several options are possible the elif statement can be used.
You may have noticed it already, but it is necessary to point out the distinction between assignment and evaluation: