Introduction to Python for Scientific Computing - Tutorial n°2

Mohammed Ait Lahcen, Economic Theory Group, Faculty of Business and Economics, University of Basel

This is the second core Python tutorial. Here we take a look at how to read (input) and write (output) to external files, how to use control flow statements and how to create user-defined functions.

For a more detailed introduction to Python for scientific computing you are encouraged to go through the lectures available at QuantEcon.org.

Input-Output

We're going to briefly look at reading and writing to text files. First we start by writing to a new file:

In [1]:
f = open('new_file.txt','w')
f.write('Hello World\n')
f.write('Hello Basel')
f.close()

where \n is the line-ending character.

The created file is by default stored in the present working directory (pwd) which can be located by typing:

In [2]:
%pwd
Out[2]:
'C:\\Users\\aitmoh00\\Dropbox\\Academic\\Teaching\\MBPS\\FS19\\Money, Banking and Payment Systems\\FS19\\Programming\\tutorials'

We can read the content of our little file

In [3]:
f = open('new_file.txt','r')
out = f.read()
out
Out[3]:
'Hello World\nHello Basel'

and print it as well

In [4]:
print(out)
Hello World
Hello Basel

Be aware of memory considerations when using the method .read() as it reads and returns the entire content of the file.

You can find more details about reading and writing to files on Python's online documentation.

Conditional statements

Conditional statements in Python use the keywords: if, elif and else:

In [5]:
x = -1

if x < 0.0:
    sign = 'negative'
elif x > 0.0:
    sign = 'positive'
else:
    sign = 'zero'
    
print('x is ' + sign)
x is negative

As you can see above, code blocks in Python are defined by the indentation level (tab).

Iterating

for loops

Loops can be programmed in Python in different ways. The most common is the for loop applied to an iterable object such as a list:

In [6]:
for j in [1,2,3]:
    print(j)
1
2
3

The range() function creates an "iterator object" on which we can iterate:

In [7]:
for j in range(4):
    print(j)
0
1
2
3
In [8]:
for j in range(-2,2):
    print(j)
-2
-1
0
1

To have access to the indices of the values when iterating over a list you can use the enumerate function:

In [9]:
l = ['a', 'b', 'c']
for i, j in enumerate(l):
    print("Letter {0} is '{1}'".format(i+1, j))
Letter 1 is 'a'
Letter 2 is 'b'
Letter 3 is 'c'

zip() is useful for iterating over pairs from two collections:

In [10]:
countries = ['Germany', 'France', 'Italy']
cities = ['Berlin', 'Paris', 'Rome']

for country, city in zip(countries, cities):
    print('The capital of {0} is {1}'.format(country,city))
The capital of Germany is Berlin
The capital of France is Paris
The capital of Italy is Rome

List comprehensions

List comprehensions are a great way of iterating in Python:

In [11]:
a = range(10)
a2 = [x**2 for x in a]
a2
Out[11]:
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

As we will see later, a lot of iterative (loop-based) calculations can also be done using Numpy vector-based operations.

while loops

The while loop keeps iterating over a code as long as the specified condition is true:

In [12]:
i = 0

while i < 5:
    print(i)
    i = i + 1
    
print("done")
0
1
2
3
4
done

User-Defined Functions

Functions are special Python objects that take inputs and return output. Creating functions in Python is very intuitive:

In [13]:
def f(i):
    i_quart = (1+i)**(1/4)-1
    return i_quart

We can write all the code we want between the first line and the return statement.

In [14]:
f(0.03)
Out[14]:
0.007417071777732875
In [15]:
f(-0.0075)
Out[15]:
-0.0018802966284235945

The output of functions returning multiple values can be unpacked as seen with tuples.

When a function is called (executed), Python creates a local namespace for that function where the objects created and manipulated during the function call live. With some exceptions, these objects are only accessible in the function's local namespace and do not carry to the global namespace:

In [16]:
def g(x):
    x = x + 1
    return x

x = 1
print('g(x) =',g(x))
print('x =',x)
g(x) = 2
x = 1

Modifications to global lists and dictionaries inside a function carry over after the function call:

In [17]:
def g(x):
    x[0] = x[0] + 1
    return x

x = [1]
print('g(x) =',g(x))
print('x =',x[0])
g(x) = [2]
x = 2

After the function returns, its local namespace is deallocated and lost.

The global namespace is accessible from the local namespace. However, if a local object has the same name as a global object the former will "shadow" the latter.

Variables in the local namespace are called local variables.

While the function is executing, we can view the contents of the local namespace with locals().

Lambda functions

Lambda (anonymous) functions are quick one-line functions that are sometimes convenient to use:

In [18]:
f = lambda x: x**2 + 4
In [19]:
f(1)
Out[19]:
5

Lambda functions can take several arguments just as standard user-defined functions.