BIO-210: Applied software engineering for life sciences
Python Introduction I - Programming Basics and Data Types#
Welcome to the course “Applied software engineering for life sciences”! During this semester you will learn how to use the programming language Python to develop a medium-sized project. The first 4 exercise sessions, including this one, will focus on teaching you the fundamentals of Python, git and VS code. Then the project phase will start!
This page (that you see right now) is called a Notebook. It is a convenient development tool, as it allows you to run your code in blocks (called cells) and immediately visualize the output. Furthermore, the variables defined in a cell are stored in memory and are readily available in all other cells. In the cell below, we are performing the operation 3 + 4. To execute it and visualize the result, just select the cell with your mouse (or moving down with an arrow) and press the play button in the command bar at the top of this page. Alternatively, you can use the shortcut [ctrl] + [enter] to run the cell ([shift] + [enter] to run the cell and move to the next one).
Specifically, this Notebook is a Jupyter Notebook a web application for creating and sharing computational documents.
3 + 4
7
If you ran the cell correctly, you should see the output “7” appearing below your cell.
Now you are ready to create your first cell. A cell can be of 3 types: Code, Markdown or Raw. To create a new cell, just press the “+” button in the command bar. Alternatively, select a cell (the cell, not its content!) and press the key “b” or “a” to create a cell below or above. By default, the cell is created in Code mode. This means that you can right away write your code and run it. You can switch mode by selecting it in the command bar, or with the shortcuts “m” for Markdown, “r” for Raw and “y” for code.
Now create a new cell below this one, write some arithmetic expressions and execute them. Do you get the expected results?
Data types - Fundamentals#
Python is a strongly, dynamically typed language. Each value has its own type: for example, 1 is an integer, while 1.0 is a float. However, variables can change their type throughout their lifetime (differently, for example, from C or C++). Here is a simple example:
x = 1
print("x = 1, type of x: ", type(x))
x = 1.
print("x = 1., type of x: ", type(x))
x = 1, type of x: <class 'int'>
x = 1., type of x: <class 'float'>
Some python operations (“+”, “-”, “*”, “%”, “**”, “//”) will preserve the data type of their inputs, while others (e.g, “/”) might change the type. Indeed, (“/”) will always return a float. See the following examples:
x = 3 + 4
print("x = 3 + 4, result: ", x, " type of x: ", type(x))
x = 3. + 4.
print("x = 3. + 4., result: ", x, " type of x: ", type(x))
x = 6 / 4
print("x = 6 / 4, result: ", x, " type of x: ", type(x))
x = 7. // 2
print("x = 7. // 2, result: ", x, " type of x: ", type(x))
x = 4 ** 3
print("x = 4 ** 3, result: ", x, " type of x: ", type(x))
x = 22.5 % 3
print("x = 22.5 % 3, result: ", x, " type of x: ", type(x))
x = 3 + 4, result: 7 type of x: <class 'int'>
x = 3. + 4., result: 7.0 type of x: <class 'float'>
x = 6 / 4, result: 1.5 type of x: <class 'float'>
x = 7. // 2, result: 3.0 type of x: <class 'float'>
x = 4 ** 3, result: 64 type of x: <class 'int'>
x = 22.5 % 3, result: 1.5 type of x: <class 'float'>
Besides integers and floats, Python supports two other fundamental data types: booleans and strings. Booleans can just store the values “True” or “False”, while strings are a more complex type, which can store characters, words and sentences (comprising lots of different characters). Here are some examples:
x = False
print("x = False, type of x: ", type(x))
x = 'a'
print("x = 'a', type of x: ", type(x))
x = 'abdc efgh'
print("x = 'abcd efgh', type of x: ", type(x))
x = False, type of x: <class 'bool'>
x = 'a', type of x: <class 'str'>
x = 'abcd efgh', type of x: <class 'str'>
You can conveniently change the type of a variable by applying one of the following builtin functions: int()
, float()
, bool()
, str()
. Pay attention: not all strings can be converted to integers, booleans or floats! And all integers and floats apart from 0 will be converted into the boolean “True”.
x = int(7.5)
print("x = int(7.5), result: ", x, " type of x: ", type(x))
x = float(3)
print("x = float(3), result: ", x, " type of x: ", type(x))
x = str(3.18)
print("x = str(3.18), result: ", x, " type of x: ", type(x))
x = int(False)
print("x = int(False), result: ", x, " type of x: ", type(x))
x = bool(234)
print("x = bool(234), result: ", x, " type of x: ", type(x))
x = int("ab")
x = int(7.5), result: 7 type of x: <class 'int'>
x = float(3), result: 3.0 type of x: <class 'float'>
x = str(3.18), result: 3.18 type of x: <class 'str'>
x = int(False), result: 0 type of x: <class 'int'>
x = bool(234), result: True type of x: <class 'bool'>
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[5], line 16
13 x = bool(234)
14 print("x = bool(234), result: ", x, " type of x: ", type(x))
---> 16 x = int("ab")
ValueError: invalid literal for int() with base 10: 'ab'
The last expression int(“ab”) triggered what is called an exception. This means that the function int()
received an “exceptional” input, which it cannot interpret as an integer. In such cases the default exception is “ValueError”. In the cell below, try to convert a string to an integer without causing any exception!
### Insert your code here
x = int("345")
print(x)
345
The string data type is very powerful. It offers many methods out of the box, which make some operations very easy. For example, you can turn letters into capital letters by calling capitalize(). The section “String Methods” of the python documentation (https://docs.python.org/3/library/stdtypes.html) gives a more general overview of all the available operations.
Exercise 1: in the cell below, use the appropriate string function to verify that the string “th” is included in the word “Python”, but that the string “tuna” is not.
### Insert your code here
s_1 = "Python"
s_2 = "th"
s_3 = "tuna"
print(f"{s_2} is in {s_1}:", s_2 in s_1)
print(f"{s_3} is in {s_1}:", s_3 in s_1)
th is in Python: True
tuna is in Python: False
Exercise 2: replace all the characters “a” in the string “abracadabra” with “u”
### Insert your code here
s = "abracadabra"
s_u = s.replace("a", "u")
print(s_u)
ubrucudubru
Exercise 3: use the function “format” to include the result of the computation 3 + 4 in the string “If you sum 3 and 4 you obtain {}”
### Insert your code here
s = "if you sum 3 and 4 you obtain {}".format(3 + 4)
print(s)
if you sum 3 and 4 you obtain 7
Apart from the string method “format” that you just used, you can also use “f-strings” to include variables/computations in strings, which improves readability of the code (among others). Below is an example of how f-strings work:
s = f"if you sum 3 and 4 you obtain {3 + 4}"
print(s)
if you sum 3 and 4 you obtain 7
Loops and ifs#
As most programming languages, Python supports for and while loops. For loops iterate through all the elements of a given object, until the end is reached or a break statement is called inside the loop body. While loops, instead, iterate until a certain condition is met. Here are 2 minimal examples:
i = 0
while i < 10:
print(i ** 2)
i += 1
0
1
4
9
16
25
36
49
64
81
for i in range(10):
print(i ** 2)
0
1
4
9
16
25
36
49
64
81
While the two code snippets produce the same output, in the first one we are manually defining the iteration count variables. In the second example, instead, we are taking advantage of the builtin function range()
, which is a special type of immutable iterable object.
To understand what that means, we need to have a look on generators and iterators:
Generator: A generator is a special type of iterable in Python that generates values on-the-fly, one at a time, and thus saves memory by not storing all values in memory at once. Hence, they are objects which can be looped through with a for loop (or by calling another builtin function,
next()
) and return one element after another. The loop asks the generator at each iteration to return the next element, and the generator executes.Iterator: An iterator is an object that represents a stream of data and can be iterated (looped) through. Iterators implement the
iter()
andnext()
methods, allowing you to access elements one by one. While range() itself is not an iterator, you can create an iterator from it using the iter() function. For example, iter(range(1, 5)) would create an iterator that generates the numbers 1, 2, 3, and 4 as you loop through it.
Python also obviously offers the possibility of assessing whether a certain condition is verified with an if - else statement. If multiple exclusive conditions are to different behavior, then one should use the elif keyword. Python supports the common binary operators <, >, ==, !=, <=, >=, or, and not. Here is a minimal example:
state = "Italy"
if state == "Italy":
print("Rome")
elif state == "France":
print("Paris")
elif state == "Germany":
print("Berlin")
elif state == "Switzerland":
print("de jure: none, de facto: Bern")
else:
print("I don't know the capital of the state", state)
Rome
Exercise 4: write some code which, given an integer, prints whether it is even or odd (hint: if you want to let a user input a number, check out the lecture slides on how to do it)
### Insert your code here
a_int = 10
if a_int % 2 == 0:
print(f"{a_int} is even")
else:
print(f"{a_int} is odd")
10 is even
Exercise 5: write some code which, given two integers, prints whether the second one is a divisor of the first one
### Insert your code here
int_1 = 45
int_2 = 15
if int_1 % int_2== 0:
print(f"{int_2} is a divisor of {int_1}")
else:
print(f"{int_2} is not a divisor of {int_1}")
15 is a divisor of 45
Exercise 6: write some code which, given two strings, prints the larger one if one of them is contained it the other, otherwise it concatenates them and prints the resulting string
### Insert your code here
string_1 = "la"
string_2 = "uhlala"
if string_1 in string_2:
print(string_2)
elif string_2 in string_1:
print(string_1)
else:
print(string_1 + string_2)
uhlala
Data types - Containers I#
The container datatypes are extremely useful in Python. Today, you will learn about lists and dictionaries, while next week you will get to know sets and tuples.
Lists#
Lists are ordered collections of values. The elements of a list can be of any type. You can retrieve an element of a list through its index (starting from 0!). There are many ways to create lists. You can generate a list directly defining its elements or by passing an iterable object (an object you can go thourgh with a for loop) to the function list()
.
x = [1, 3, 5, 6]
print("x = [1, 3, 5, 6] , type of x: ", type(x))
x_2 = x[2]
print("The element of the list in position 2 is", x_2) # note this is the third element!
x = list(range(10))
print("List including the 10 digits: ", x)
x = [1, 3, 5, 6] , type of x: <class 'list'>
The element of the list in position 2 is 5
List including the 10 digits: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Lists support many useful operations, such as append()
and extend()
:
x = [1, 4, 6]
print("Base list: ", x)
x.append(8)
print("After appending 8: ", x)
x.extend([3, 5])
print("And after extending it by [3, 5]: ", x)
Base list: [1, 4, 6]
After appending 8: [1, 4, 6, 8]
And after extending it by [3, 5]: [1, 4, 6, 8, 3, 5]
Lists can be conveniently looped through with a for statement. Besides the standard syntax, they also support what is called the list comprehension syntax, which you can use, for example, to generate a new list starting from an existing one, or from a generator.
x = [1, 3, 5, 6]
x_squared = []
for el in x:
x_squared.append(el**2)
print(x_squared)
[1, 9, 25, 36]
x = [1, 3, 5, 6]
x_squared = [el ** 2 for el in x]
print(x_squared)
[1, 9, 25, 36]
Exercise 7. Print the last two elements of the French-speaking cities in the following nested list and then remove the element that does not represent a city of Switzerland.
cities = [["Lausanne", "Geneva", "Sion"], ["Bern", "Basel", "Zürich"], "Auckland", ["Lugano"]]
### Insert your code here
print(cities[0][-2:])
cities.pop(2)
print(cities)
['Geneva', 'Sion']
[['Lausanne', 'Geneva', 'Sion'], ['Bern', 'Basel', 'Zürich'], ['Lugano']]
Exercise 8. Compute the mean and the variance of all the numbers in the following list.
numbers = [5, 11, 4, 0, 3, 23, 41, 2, 25, 42, 13, 7, 4, 14, 19]
### Insert your code here
list_sum = 0
list_len = 0
for el in numbers:
list_sum += el
list_len += 1
mean = list_sum / list_len
var = 0
for el in numbers:
var += (el-mean)**2
var /= list_len
print('mean:', mean)
print('variance:', var)
mean: 14.2
variance: 169.36
Dictionaries#
Dictionaries are another useful built-in datatype. They simply represent a map between two sets: the keys and the values. They are the natural representation of a .json file in a python object. Dictionaries, similarly to lists and sets, can be created by direct definition or by calling the function dict() on an iterable object (this time the elements of the iterable object must have two values each - the key and the value!)
x = {
"dog": "woof",
"cat": "meow",
"pig": "oink",
"cow": "moo"
}
print("Animal to sound:", x)
x = dict([["dog", "woof"], ["cat", "meow"], ["pig", "oink"], ["cow", "moo"]])
print("The same dict, but defined in another way: ", x)
Animal to sound: {'dog': 'woof', 'cat': 'meow', 'pig': 'oink', 'cow': 'moo'}
The same dict, but defined in another way: {'dog': 'woof', 'cat': 'meow', 'pig': 'oink', 'cow': 'moo'}
Accessing the values stored in a dictionary can be done by key (through the function .get() or through square brackets):
sound = x["dog"]
print("The dog's call is", sound)
sound = x.get("cow")
print("The cow's call is", sound)
The dog's call is woof
The cow's call is moo
To add an element to a dictionary or to change the value associated to a certain key you can either use the square brackets or the function update()
x["donkey"] = "hee-haw"
x.update({"owl": "hoot"})
print(x)
{'dog': 'woof', 'cat': 'meow', 'pig': 'oink', 'cow': 'moo', 'donkey': 'hee-haw', 'owl': 'hoot'}
You can access the keys of your dictionary with the function keys()
which returns a dict_keys object. For simplicity, you can convert this object in a list using the function list()
keyset = list(x.keys())
print("The keys in the dictionary are: ", keyset)
The keys in the dictionary are: ['dog', 'cat', 'pig', 'cow', 'donkey', 'owl']
And of course you can iterate through a dictionary. Calling a for loop through a dictionary actually iterates through its keyset. If you want to iterate through the dictionary’s keys and values simultaneously, you can use the handy method items()
for key in x:
print("The iteration variable is: ", key)
print("And I can use it to access the value: ", x[key])
print('')
print("But I can also access the key and the value together")
for key, value in x.items():
print("key: {}, value: {}".format(key,value))
The iteration variable is: dog
And I can use it to access the value: woof
The iteration variable is: cat
And I can use it to access the value: meow
The iteration variable is: pig
And I can use it to access the value: oink
The iteration variable is: cow
And I can use it to access the value: moo
The iteration variable is: donkey
And I can use it to access the value: hee-haw
The iteration variable is: owl
And I can use it to access the value: hoot
But I can also access the key and the value together
key: dog, value: woof
key: cat, value: meow
key: pig, value: oink
key: cow, value: moo
key: donkey, value: hee-haw
key: owl, value: hoot
Important! Dictionaries are not ordered collections, so it is not possible to get their elements by index.
Exercise 9. Create a dictionary where the keys are numbers between 1 and 10 (both included) and the values are the square of the keys.
### Insert your code here
mydict = dict()
for x in range(1,11):
mydict[x] = x**2
print(mydict)
{1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81, 10: 100}
General exercises#
Now it’s time to use all the concepts you have learned today and put them together!
Exercise 10. Write some code which, given an input integer N, computes all the prime numbers up to N and stores them in a list.
### Insert your code here
n = 1000
survivors = list(range(2, n + 1))
primes = []
while survivors:
primes.append(survivors[0])
survivors = [s for s in survivors if s % survivors[0]]
print(primes)
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997]
Exercise 11 (BONUS). This exercise is a little two-player game. Write an algorithm that guesses a particular number that the other player knows in the fastest possible way. For simplicity, you can use the random
package to generate a random number from the interval [1,100] to select the guessed number. After guessing a number, your program should tell you if the guessed number is the correct one, or if it is smaller or greater than the target one. Then, based on this information, you guess a new number and perform the previous steps in an iterative way. Count the number of steps until you found the number.
import random
target = random.randint(1, 100)
### Insert your code here
print("Target: ", target)
max_it = 100
guess = 50
lim_right = 100
lim_left = 1
i = 0
while guess != target and i < max_it:
if target > guess:
lim_left = guess
guess = int((lim_right + guess) / 2)
else:
lim_right = guess
guess = int((lim_left + guess) / 2)
i += 1
print("Number of iterations: ", i, "guess: ", guess)
Target: 33
Number of iterations: 6 guess: 33