Commit 05a72127 authored by paugier's avatar paugier
Browse files

Change title notebooks

parent 07c31ad7
Pipeline #49931 passed with stage
in 37 seconds
%% Cell type:markdown id: tags:
# Python training UGA 2017
# Scientific computing course 2020
**A training to acquire strong basis in Python to use it efficiently**
**Acquire strong basis in Python to use it efficiently**
Pierre Augier (LEGI), Cyrille Bonamy (LEGI), Eric Maldonado (Irstea), Franck Thollard (ISTerre), Christophe Picard (LJK), Loïc Huder (ISTerre)
# Standard types and basic statements
%% Cell type:markdown id: tags:
## Function calls
There are [built-in functions](https://docs.python.org/3/library/functions.html) and the developers can of course define other functions. To call a function:
%% Cell type:code id: tags:
``` python
print("hello")
```
%% Output
hello
%% Cell type:markdown id: tags:
Some functions return a result.
%% Cell type:code id: tags:
``` python
round(1.2)
```
%% Output
1
%% Cell type:markdown id: tags:
It's common to store the result in a variable:
%% Cell type:code id: tags:
``` python
my_var = round(1.2)
```
%% Cell type:markdown id: tags:
which can then be used:
%% Cell type:code id: tags:
``` python
print(my_var)
```
%% Output
1
%% Cell type:markdown id: tags:
## Few standard types
- Simple types (`int`, `float`, `bool`, `complex`)
- Standard type `str`
- Standard type `list`
- Standard type `tuple`
%% Cell type:markdown id: tags:
### `int` (integers)
%% Cell type:code id: tags:
``` python
a = 4
c = -10
# binary notation (base 2)
b = 0b010
# octal notation (base 8)
o = 0o011
# hexadecimal (base 16)
h = 0x1cf0
a = int('1') # base 10
a = int('111', 2) # base 2
a = int('70', 8) # base 8
a = int('16', 16) # base 16
```
%% Cell type:markdown id: tags:
Remark: `int` in Python 3 are impressive! No limit! See https://docs.python.org/3.1/whatsnew/3.0.html#integers
%% Cell type:markdown id: tags:
## Arithmetic operations
%% Cell type:code id: tags:
``` python
print(10 + 3)
print(10 - 3)
print(10 * 3)
print(10 / 3) # float division
print(10 // 3) # integer division
print(10 % 3)
```
%% Output
13
7
30
3.3333333333333335
3
1
%% Cell type:markdown id: tags:
### `bool` (booleans)
%% Cell type:code id: tags:
``` python
b = bool('1')
b = False
b = True
```
%% Cell type:markdown id: tags:
##### Comparison operations (bool)
- `==` equal
- `!=` différent
- `<` inferior
- `<=` inferior or equal
- `>` superior
- `>=` superior or equal
%% Cell type:markdown id: tags:
##### Keyword `is`: check identity
%% Cell type:code id: tags:
``` python
a = None
print(a is None)
print(a is not None)
```
%% Output
True
False
%% Cell type:markdown id: tags:
##### Keywords `and` and `or`
%% Cell type:code id: tags:
``` python
True and True
```
%% Output
True
%% Cell type:code id: tags:
``` python
True and False
```
%% Output
False
%% Cell type:code id: tags:
``` python
False and False
```
%% Output
False
%% Cell type:code id: tags:
``` python
True or True
```
%% Output
True
%% Cell type:code id: tags:
``` python
True or False
```
%% Output
True
%% Cell type:code id: tags:
``` python
False or False
```
%% Output
False
%% Cell type:markdown id: tags:
### `float` (real, double precision) and `complex`
%% Cell type:code id: tags:
``` python
# float
a = float('1')
a = 1.234
a = 1e2
a = -1e-2
a = .2
```
%% Cell type:code id: tags:
``` python
# complex (2 floats)
c = complex('1')
c = 1 + 2j
print(c, c.real, c.imag)
```
%% Output
(1+2j) 1.0 2.0
%% Cell type:markdown id: tags:
Remark: notation `var_name.attr_name` to access to an attribute of an object.
%% Cell type:markdown id: tags:
## Warning about floating-point arithmetic and numerical errors!
%% Cell type:code id: tags:
``` python
b = 1e16
c = 1.2 + b
d = c - b
print(d)
```
%% Output
2.0
%% Cell type:markdown id: tags:
Very general issue (not Python):
see https://en.wikipedia.org/wiki/Floating-point_arithmetic
%% Cell type:markdown id: tags:
### Standard type `str`
%% Cell type:code id: tags:
``` python
s = 'hello'
s = "hello"
s = ('How is it possible to write a very very '
'very long string with lines limited to 79 characters?')
s = """Strings on
more thand
one line.
"""
print(s)
```
%% Output
Strings on
more thand
one line.
%% Cell type:markdown id: tags:
Warning: big difference between Python 2 and Python 3. In Python 3, `str` are unicode and there is another type `bytes`.
%% Cell type:markdown id: tags:
#### Methods of the type `str`
Objects of built-in types have methods associated with their type (object oriented programming). The built-in function `dir` returns a list of name of the attributes. For a string, these attributes are python system attributes (with double-underscores) and several public methods:
%% Cell type:code id: tags:
``` python
s = 'abcdef'
print(dir(s))
```
%% Output
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
%% Cell type:markdown id: tags:
To access an attribute of an object (here, the method `str.startswith`), we use the dot:
%% Cell type:code id: tags:
``` python
s.startswith('a')
```
%% Output
True
%% Cell type:markdown id: tags:
### standard type `str`
#### function `str.format`
```text
Docstring:
S.format(*args, **kwargs) -> str
Return a formatted version of S, using substitutions from args and kwargs.
The substitutions are identified by braces ('{' and '}').
```
%% Cell type:code id: tags:
``` python
a = 1.23456789
'a = {}'.format(a)
```
%% Output
'a = 1.23456789'
%% Cell type:code id: tags:
``` python
'a = {:.4f}'.format(a)
```
%% Output
'a = 1.2346'
%% Cell type:code id: tags:
``` python
'a = {:8.4f}'.format(a)
```
%% Output
'a = 1.2346'
%% Cell type:code id: tags:
``` python
'a = {:.4e} (scientific notation)'.format(a)
```
%% Output
'a = 1.2346e+00 (scientific notation)'
%% Cell type:code id: tags:
``` python
print('{}\t{}\t{}'.format(1, 2, 3))
```
%% Output
1 2 3
%% Cell type:markdown id: tags:
### standard type `str`
#### New in Python 3.6: format strings
%% Cell type:code id: tags:
``` python
a = 1.23456789
f'a = {a}'
```
%% Output
'a = 1.23456789'
%% Cell type:code id: tags:
``` python
f'a = {a:.4f}'
```
%% Output
'a = 1.2346'
%% Cell type:code id: tags:
``` python
f'a = {a:8.4f}'
```
%% Output
'a = 1.2346'
%% Cell type:code id: tags:
``` python
f'a = {a:.4e} (scientific notation)'
```
%% Output
'a = 1.2346e+00 (scientific notation)'
%% Cell type:code id: tags:
``` python
print(f'{1}\t{1+1}\t{2+1}')
```
%% Output
1 2 3
%% Cell type:markdown id: tags:
### standard type `str`
Strings are immutable **["sequences"](https://docs.python.org/3/library/stdtypes.html)**.
- lookup
%% Cell type:code id: tags:
``` python
s = 'abcdef'
print('a' in s)
print('hello' not in s)
```
%% Output
True
True
%% Cell type:markdown id: tags:
- We can get an element of a string (**index starts from 0**):
%% Cell type:code id: tags:
``` python
print(s[0])
```
%% Output
a
%% Cell type:markdown id: tags:
- since strings are immutable, they can not be modified inplace. If we try, we get an error:
```python
s[0] = 'b'
```
```text
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-5-55620f378bce> in <module>()
----> 1 s[0] = 'b'
TypeError: 'str' object does not support item assignment
```
- since strings are sequences, they can be "sliced" (we will soon study in details this powerful notation):
%% Cell type:code id: tags:
``` python
s[1:3]
```
%% Output
'bc'
%% Cell type:markdown id: tags:
- it is very simple to manipulate strings in many ways:
%% Cell type:code id: tags:
``` python
print((s.capitalize() + ' ' + s.upper() + '\n') * 4 )
```
%% Output
Abcdef ABCDEF
Abcdef ABCDEF
Abcdef ABCDEF
Abcdef ABCDEF
%% Cell type:markdown id: tags:
### Slicing
Very general, can be used on all sequences as `str`, `list`, etc... Not simple for beginners but very powerfull (see [here](http://stackoverflow.com/questions/509211/explain-pythons-slice-notation) and [here](http://avilpage.com/2015/03/a-slice-of-python-intelligence-behind.html)).
Python indexes and slices for a six-element str. Indexes enumerate the elements, slices enumerate the spaces between the elements.
```text
Index from rear: -6 -5 -4 -3 -2 -1
Index from front: 0 1 2 3 4 5
+---+---+---+---+---+---+
| a | b | c | d | e | f |
+---+---+---+---+---+---+
Slice from front: 0 1 2 3 4 5 6
Slice from rear: -6 -5 -4 -3 -2 -1 None
```
%% Cell type:code id: tags:
``` python
s = 'abcdef'
# s[start:stop:step]
s[2:6:2]
```
%% Output
'ce'
%% Cell type:code id: tags:
``` python
# s[start:stop]
s[2:6]
```
%% Output
'cdef'
%% Cell type:code id: tags:
``` python
# s[start:]
s[1:]
```
%% Output
'bcdef'
%% Cell type:code id: tags:
``` python
# s[:stop]
s[:2]
```
%% Output
'ab'
%% Cell type:code id: tags:
``` python
# step = -1: goes through the string in reverse order
s[::-1]
```
%% Output
'fedcba'
%% Cell type:markdown id: tags:
### Do it yourself
Suppose we have a string representing a header line of the form:
%% Cell type:code id: tags:
``` python
s = ' wind;temperature;;pressure '
```
%% Cell type:markdown id: tags:
- Remove leading and ending blanks (see `str.replace` and `str.strip`)
- Extract the first field (e.g. using `find` method and slicing)
- Extract the last field (e.g. using `rfind` method and slicing)
- Check for empty field (e.g. `find` ";;" pattern)
- Remove empty field (e.g. using replace method)
- BONUS : Extract the second field (tip :`find` can take an argument that tells where to start the search)
%% Cell type:markdown id: tags:
#### A possible solution:
%% Cell type:code id: tags:
``` python
s = ' wind;temperature;;pressure '
# remove leading blanks
s = s.strip()
print("--{}--".format(s))
# extract the first field
idx = s.find(";")
s0 = s[0:idx]
print(s0)
# extract the second field
idx1 = s.find(";", idx+1) # start the search after the first ";"
s1 = s[idx+1:idx1]
print(s1)
# extract the last field
idx2 = s.rfind(";")
s2 = s[idx2+1:]
print(s2)
idx_first_empty_field = s.find(";;")
print(idx_first_empty_field)
# remove empty field
s_no_empty = s.replace(";;", ";")
print(s_no_empty)
```
%% Output
--wind;temperature;;pressure--
wind
temperature
pressure
16
wind;temperature;pressure
%% Cell type:markdown id: tags:
### standard type `list`
A list is a **mutable sequence of (possibly inhomogeneous) elements**.
%% Cell type:code id: tags:
``` python
type([0, 'a'])
```
%% Output
list
%% Cell type:code id: tags:
``` python
# create an empty list
l = []
# fill the list (with the function append)
l.append('2')
# fill the list (with the function extend)
l.extend([6, 3.])
print(l)
```
%% Output
['2', 6, 3.0]
%% Cell type:code id: tags:
``` python
# concatenate lists with the operator +
print(l + ['hello', 3])
```
%% Output
['2', 6, 3.0, 'hello', 3]
%% Cell type:code id: tags:
``` python
# get values
print(l[0], l[2], l[-2])
# slicing
print(l[0:2])
```
%% Output
2 3.0 6
['2', 6]
%% Cell type:markdown id: tags:
### standard type `tuple`
A tuple is a **immutable sequence of (possibly inhomogeneous) elements**.
Remark: when you need a sequence that won't be modified, tuple is usually more efficient than list.
%% Cell type:code id: tags:
``` python
t = 0, 'a', 1.2
t1 = (5, 'hello')
t2 = tuple([1.1, 2])
type(t)
```
%% Output
tuple
%% Cell type:code id: tags:
``` python
t[1] # indexing
```
%% Output
'a'
%% Cell type:code id: tags:
``` python
t[1:] # slicing
```
%% Output
('a', 1.2)
%% Cell type:code id: tags:
``` python
a, b = t1 # tuple assigment
print(b)
```
%% Output
hello
%% Cell type:markdown id: tags:
## Mutable and immutable objects
The objects of type `str`, `int`, `float`, `bool` are immutable. They can not be modified. Of course, a name that points towards an integer can point towards a different integer.
%% Cell type:code id: tags:
``` python
i = 1
i = i + 2 # (or i += 2)
print(i)
i = 10
print(i)
```
%% Output
3
10
%% Cell type:markdown id: tags:
Here, the objects `1` and `3` have not been modified.
%% Cell type:markdown id: tags:
## Mutable and immutable objects
An object of type `list` is mutable:
%% Cell type:code id: tags:
``` python
l = [0, 5]
print(l)
l.append('hello')
print(l)
```
%% Output
[0, 5]
[0, 5, 'hello']
%% Cell type:markdown id: tags:
Here, the object list tagged by the name `l` has been modified inplace.
%% Cell type:markdown id: tags:
## References and `del` keyword
`del` removes a reference. If an object in not binded to any names, Python can delete it from its internal memory.
%% Cell type:code id: tags:
``` python
l = ['a', 'b']
del l[1]
print(l)
```
%% Output
['a']
%% Cell type:markdown id: tags:
### More on slicing
Very general, can be used on all sequences as `str`, `list`, etc... Not simple for beginners but very powerfull (see [here](http://stackoverflow.com/questions/509211/explain-pythons-slice-notation) and [here](http://avilpage.com/2015/03/a-slice-of-python-intelligence-behind.html)).
Python indexes and slices for a six-element str. Indexes enumerate the elements, slices enumerate the spaces between the elements.
```text
Index from rear: -6 -5 -4 -3 -2 -1
Index from front: 0 1 2 3 4 5
+---+---+---+---+---+---+
| a | b | c | d | e | f |
+---+---+---+---+---+---+
Slice from front: 0 1 2 3 4 5 6
Slice from rear: -6 -5 -4 -3 -2 -1 0
```
%% Cell type:code id: tags:
``` python
s = 'abcdef'
# s[start:stop:step]
s[2:6:2]
```
%% Output
'ce'
%% Cell type:markdown id: tags:
### More on slicing
#### Assigment to mutable object
%% Cell type:code id: tags:
``` python
l = [0, 1, 2, 3, 4, 5]
l1 = l # assigment to a new name l1 (no copy of the object).
# the names l and l1 points towards the same object.
l1.append('a')
print(l1)
print(l)
```
%% Output
[0, 1, 2, 3, 4, 5, 'a']
[0, 1, 2, 3, 4, 5, 'a']
%% Cell type:markdown id: tags:
#### Shallow copy
%% Cell type:code id: tags:
``` python
l = [0, 1, 2, 3, 4, 5]
l1 = l[:] # shallow copy of l
l1.append('a')
print(l1)
print(l)
```
%% Output
[0, 1, 2, 3, 4, 5, 'a']
[0, 1, 2, 3, 4, 5]
%% Cell type:markdown id: tags:
### More on slicing
Other examples of slices for a six-element list. Indexes enumerate the elements, slices enumerate the spaces between the elements.
%% Cell type:code id: tags:
``` python
a = [0, 1, 2, 3, 4, 5]
all([
len(a) == 6,
a[1:] == [1, 2, 3, 4, 5],
a[:5] == [0, 1, 2, 3, 4],
a[0] == 0,
a[:-2] == [0, 1, 2, 3],
a[5] == 5,
a[1:2] == [1],
a[-1] == 5,
a[1:-1] == [1, 2, 3, 4],
a[-2] == 4,
])
```
%% Output
True
%% Cell type:markdown id: tags:
### Do it yourself
Suppose we have the string containing header line.
%% Cell type:code id: tags:
``` python
s = 'wind;temperature;pressure'
```
%% Cell type:markdown id: tags:
- Extract the list of items (i.e. "wind", "temperature", "pressure"; see `str.split`).
- Add "Snow level" to the list of items (e.g. using append)
- Build a new header such that items are capitalized (e.g. using the methods `str.join` and `str.capitalize` and iterating on the list)
%% Cell type:markdown id: tags:
#### A possible solution:
%% Cell type:code id: tags:
``` python
s = 'wind;temperature;pressure'
list_items = s.split(";")
print(list_items)
list_items.append("snow level".capitalize())
list_items[0] = list_items[0].capitalize()
list_items[1] = list_items[1].capitalize()
list_items[2] = list_items[2].capitalize()
print(list_items)
";".join(list_items)
```
%% Output
['wind', 'temperature', 'pressure']
['Wind', 'Temperature', 'Pressure', 'Snow level']
'Wind;Temperature;Pressure;Snow level'
%% Cell type:markdown id: tags:
### The function `range`
The function returns a range object:
%% Cell type:code id: tags:
``` python
# start, stop, step
range(1, 8, 2)
```
%% Output
range(1, 8, 2)
%% Cell type:markdown id: tags:
We can make a list with the range object:
%% Cell type:code id: tags:
``` python
# start, stop, step
list(range(1, 8, 2))
```
%% Output
[1, 3, 5, 7]
%% Cell type:code id: tags:
``` python
# start, stop (step=1)
list(range(2, 8))
```
%% Output
[2, 3, 4, 5, 6, 7]
%% Cell type:code id: tags:
``` python
# stop argument (start=0, step=1)
list(range(8))
```
%% Output
[0, 1, 2, 3, 4, 5, 6, 7]
%% Cell type:markdown id: tags:
### Do it yourself
Build a list of odd numbers in decreasing order.
%% Cell type:markdown id: tags:
## Conditions: `if`, `elif`, `else`
```python
if expression:
statement(s)
else:
statement(s)
```
The statement contains the block of code that executes if the conditional expression in the if statement resolves to 1 or a TRUE value.
%% Cell type:code id: tags:
``` python
a = 0
if a == 0:
print('a is equal to 0.')
```
%% Output
a is equal to 0.
%% Cell type:code id: tags:
``` python
a = 1
if a < 0:
print('a is negative.')
elif a == 0:
print('a is equal to 0.')
elif a > 0:
print('a is positive.')
else:
print("I don't know.")
```
%% Output
a is positive.
%% Cell type:markdown id: tags:
## Loops
%% Cell type:markdown id: tags:
### Loops with the keyword `while`
%% Cell type:code id: tags:
``` python
i = 0
while i < 4:
i += 1
print('i =', i)
```
%% Output
i = 4
%% Cell type:code id: tags:
``` python
i = 0
while i < 4:
i += 1
print('i =', i)
```
%% Output
i = 1
i = 2
i = 3
i = 4
%% Cell type:markdown id: tags:
### Do it yourself
- Edit a script with the spyder editor that calculates the average of a set of numbers. For example `numbers = [67, 12, 2, 9, 23, 5]`
* using the functions `sum` and `len`
* manually, using the keyword `while`
- Run the script
* in spyder,
* in a ipython session opened from another terminal,
* with the command `python`.
%% Cell type:markdown id: tags:
#### A possible solution:
%% Cell type:code id: tags:
``` python
numbers = [67, 12, 2, 9, 23, 5]
local_sum = 0
i = 0
while i < len(numbers):
local_sum = local_sum + numbers[i]
i = i+1
avg = local_sum / len(numbers)
print(avg)
```
%% Output
19.666666666666668
%% Cell type:markdown id: tags:
### Loops with the keyword `for`
%% Cell type:code id: tags:
``` python
values = range(5)
for value in values:
print(value, end=', ')
```
%% Output
0, 1, 2, 3, 4,
%% Cell type:code id: tags:
``` python
# the built-in function enumerate is very useful
for index, value in enumerate(['a', 'b', 'c']):
print('({}, {})'.format(index, value))
```
%% Output
(0, a)
(1, b)
(2, c)
%% Cell type:markdown id: tags:
### Loops: keywords `continue` and `break`
- `continue`: passes the block in the loop and continues the loop.
%% Cell type:code id: tags:
``` python
for x in range(1, 8):
if x == 5:
continue
print(x, end=', ')
```
%% Output
1, 2, 3, 4, 6, 7,
%% Cell type:markdown id: tags:
- `break`: stop the loop.
%% Cell type:code id: tags:
``` python
for x in range(1, 11):
if x == 5:
break
print(x, end=', ')
```
%% Output
1, 2, 3, 4,
%% Cell type:markdown id: tags:
### Do it yourself
- Simplify your script by using a `for` loops.
- In ipython, try to understand how the function `enumerate` works. Use it in your script.
%% Cell type:markdown id: tags:
#### A possible solution:
%% Cell type:code id: tags:
``` python
l = [67, 12, 2, 9, 23, 5]
local_sum = 0
for e in l:
local_sum += e
avg = local_sum / len(l)
print(avg)
```
%% Output
19.666666666666668
%% Cell type:markdown id: tags:
### Do it yourself
We build a list:
%% Cell type:code id: tags:
``` python
from random import shuffle, randint
n = 20
i = randint(0, n-1)
print('integer remove from the list:', i)
l = list(range(n))
l.remove(i)
shuffle(l)
print('shuffled list: ', l)
```
%% Output
integer remove from the list: 3
shuffled list: [13, 16, 6, 10, 17, 19, 4, 2, 5, 9, 0, 7, 11, 12, 14, 8, 1, 15, 18]
%% Cell type:markdown id: tags:
One element has been removed:
- Find this element (given that you can change the ordering of `l`).
- Find this element (given that you cannot change the ordering of `l`).
%% Cell type:markdown id: tags:
#### A possible solution:
%% Cell type:code id: tags:
``` python
# we can change ordering, let's sort
print(l)
l_sorted = sorted(l)
print(l_sorted)
found = None
for idx, elem in enumerate(l_sorted):
if elem != idx:
found = idx
break
if found is None:
found = len(l)
print("missing ", idx)
```
%% Output
[13, 16, 6, 10, 17, 19, 4, 2, 5, 9, 0, 7, 11, 12, 14, 8, 1, 15, 18]
[0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
missing 3
%% Cell type:code id: tags:
``` python
# we cannot sort -> higher complexity
for elem in range(len(l)+1):
if elem not in l:
break
print("missing ", elem)
```
%% Output
missing 3
%% Cell type:code id: tags:
``` python
# another solution
actual_sum = sum(l)
len_l = len(l)
original_sum = (len_l + 1) * (len_l) // 2
print("missing ", original_sum - actual_sum)
```
%% Output
missing 3
%% Cell type:markdown id: tags:
## Exceptions and `try`, `except` syntax
%% Cell type:markdown id: tags:
Exceptions and errors are common in all codes. There is a good system to handle them in Python. Let's first see what gives such buggy code
```python
l = ['a']
i = 1
print(l[i])
```
When these lines are executed, Python stops its execution and print a traceback:
```text
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-30-8df9cec1a0ec> in <module>()
1 l = ['a']
2 i = 1
----> 3 print(l[i])
IndexError: list index out of range
```
%% Cell type:markdown id: tags:
## Exceptions and `try`, `except` syntax
Handling exception:
%% Cell type:code id: tags:
``` python
l = ['a']
i = 1
try:
print(l[i])
except IndexError as e:
print(e)
```
%% Output
list index out of range
%% Cell type:markdown id: tags:
Remark: never use
```python
except:
```
It means "except all errors and exceptions". A user Control-C is an exception (`KeyboardInterrupt`) so it would be caught and have no effect.
%% Cell type:markdown id: tags:
## Full syntax
```python
try:
...
except <exception1> as e1:
...
except <exception2> as e2:
...
else:
...
finally:
...
```
[Non exhaustive error list](https://docs.python.org/3/library/exceptions.html):
- ArithmeticError
- ZeroDivisionError
- IndexError
- KeyError
- AttributeError
- IOError
- ImportError
- NameError
- SyntaxError
- TypeError
%% Cell type:markdown id: tags:
### Do it yourself:
For each line of this string, append a variable of correct type in a list (i.e. "hello" should stay hello,
2 should become an int and 1.5 a float). Do it by catching errors.
**Hints:**
- int("a") and float("a") raise ValueError
- the above str_variable can be split using "\n"
%% Cell type:code id: tags:
``` python
str_variables = """hello
1.5
2"""
the_list_you_should_get = ["hello", 1.5, 2]
```
%% Cell type:markdown id: tags:
#### A possible solution:
%% Cell type:code id: tags:
``` python
split_list = []
for value in str_variables.split("\n"):
try:
value = int(value)
except ValueError:
print(value, "is not a int")
try:
value = float(value)
except ValueError:
print(value, 'is not a float')
split_list.append(value)
print(split_list)
```
%% Output
hello is not a int
hello is not a float
1.5 is not a int
['hello', 1.5, 2]
......
%% Cell type:markdown id: tags:
# Python training UGA 2017
# Scientific computing course 2020
**A training to acquire strong basis in Python to use it efficiently**
**Acquire strong basis in Python to use it efficiently**
Pierre Augier (LEGI), Cyrille Bonamy (LEGI), Eric Maldonado (Irstea), Franck Thollard (ISTerre), Christophe Picard (LJK), Loïc Huder (ISTerre)
# Functions
A function is a block of organized, reusable code that is used to perform a single, related action. Functions provide better modularity for your application and a high degree of code
reusing.
%% Cell type:markdown id: tags:
## Simple function definitions and calls
Function blocks begin with the keyword `def` followed by the function name and parentheses (`()`).
- The code block within every function starts with a colon (:) and is indented.
- Any input parameters or arguments should be placed within these parentheses.
%% Cell type:code id: tags:
``` python
def print_hello():
"hello printer"
print('hello')
def myprint(s):
"my hello printer"
print('I print', s)
# function calls
print_hello()
print_hello()
myprint('First call of myprint')
myprint('Second call of myprint')
```
%% Output
hello
hello
I print First call of myprint
I print Second call of myprint
%% Cell type:markdown id: tags:
## Simple function definitions and calls
- The first statement of a function can be the documentation string of the function, also called "docstring".
- The statement `return [expression]` exits a function, optionally passing back an expression to the caller. No return statement or a return statement with no arguments is the same as `return None`.
(Note: Wikipedia about duck typing: https://fr.wikipedia.org/wiki/Duck_typing)
%% Cell type:code id: tags:
``` python
import unittest
def add(arg0, arg1):
"""Print and return the sum of the two arguments (duck typing)."""
result = arg0 + arg1
print('result = ', result)
return result
class TestMyStuff(unittest.TestCase):
def test_add(self):
""" test max_temp computes correctly"""
self.assertEqual(8, add(3, 5))
self.assertEqual("aabb", add("aa", "bb"))
_res = unittest.TextTestRunner(verbosity=2).run(unittest.TestLoader().loadTestsFromTestCase(TestMyStuff0))
```
%% Output
test_add (__main__.TestMyStuff0)
test max_temp computes correctly ...
result = 5
result = aabb
ok
----------------------------------------------------------------------
Ran 1 test in 0.004s
OK
%% Cell type:code id: tags:
``` python
add(2, 3)
```
%% Output
result = 5
5
%% Cell type:code id: tags:
``` python
add('a', 'b')
```
%% Output
result = ab
'ab'
%% Cell type:markdown id: tags:
### Do it yourself: simple function definition
Write a function that returns the sum of the first argument with twice the second argument.
%% Cell type:code id: tags:
``` python
import unittest
def add_second_twice(arg0, arg1):
"""Return the sum of the first argument with twice the second one.
Arguments should be of type that support sum and product by
an integer (e.g. numerical, string, list, ...)
:param arg0: first argument
:param arg1: second argument
:return: arg0 + 2 * arg1
"""
pass
class TestMyStuff0(unittest.TestCase):
def test_add_second_twice(self):
""" test max_temp computes correctly"""
self.assertEqual(13, add_second_twice(3, 5))
self.assertEqual("aabbbb", add_second_twice("aa", "bb"))
self.assertListEqual([1, 2, 3, 4, 3, 4], add_second_twice([1, 2], [3, 4]))
_res = unittest.TextTestRunner(verbosity=2).run(unittest.TestLoader().loadTestsFromTestCase(TestMyStuff0))
```
%% Output
test_add_second_twice (__main__.TestMyStuff0)
test max_temp computes correctly ...
arg0 + 2*arg1 = 3 + 2*5 = 13
arg0 + 2*arg1 = aa + 2*bb = aabbbb
arg0 + 2*arg1 = [1, 2] + 2*[3, 4] = [1, 2, 3, 4, 3, 4]
ok
----------------------------------------------------------------------
Ran 1 test in 0.003s
OK
%% Cell type:markdown id: tags:
### Do it yourself: simple function definition
**A solution:**
%% Cell type:code id: tags:
``` python
def add_second_twice(arg0, arg1):
"""Return the sum of the first argument with twice the second one.
Arguments should be of type that support sum and product by
an integer (e.g. numerical, string, list, ...)
:param arg0: first argument
:param arg1: second argument
:return: arg0 + 2 * arg1
"""
result = arg0 + 2*arg1
print(f'arg0 + 2*arg1 = {arg0} + 2*{arg1} = {result}')
return result
myfunc(4, 6)
```
%% Output
arg0 + 2*arg1 = 4 + 2*6 = 16
16
%% Cell type:code id: tags:
``` python
myfunc('a', 'b')
```
%% Output
arg0 + 2*arg1 = a + 2*b = abb
'abb'
%% Cell type:markdown id: tags:
## All Python functions return exactly one object but... `None` and `tuple`
%% Cell type:code id: tags:
``` python
type(print())
```
%% Output
NoneType
%% Cell type:code id: tags:
``` python
def return_a_tuple():
return 1, 'hello', 3 # a tuple, same as (1, 'hello', 3)
my_tuple = return_a_tuple()
print(my_tuple)
```
%% Output
(1, 'hello', 3)
%% Cell type:code id: tags:
``` python
a, b, c = return_a_tuple()
print(b)
```
%% Output
hello
%% Cell type:markdown id: tags:
## Function call: namespaces and objects "passed by references"
For each function call:
- a **new namespace** is created (as at the beginning of a module)
- the objects are **"passed by references"**: new names of the arguments are created in the function namespace and they point towards the objects given as arguments to the function (so it is possible to modify the mutable objects).
%% Cell type:markdown id: tags:
## Function call: objects "passed by references"
Exercice: use 2 schemes "namespaces-objects" to understand these 2 pieces of code.
%% Cell type:code id: tags:
``` python
number = 2
mylist = []
def my_strange_append_square(l, n):
# new function namespace with names "l" and "n"
n = n**2
l.append(n)
my_strange_append_square(mylist, number)
print(mylist, number)
```
%% Output
[4] 2
%% Cell type:code id: tags:
``` python
number = 2
mylist = []
def my_strange_append_square(mylist, number):
# new function namespace with names "mylist" and "number"
number = number**2
mylist.append( number)
my_strange_append_square(mylist, number)
print(mylist, number)
```
%% Output
[4] 2
%% Cell type:markdown id: tags:
## Global vs Local variables
Variables that are defined inside a function body have a local scope (i.e. are defined in the function namespace), and those defined outside have a global scope.
This means that local variables can be accessed only inside the function in which they are declared, whereas global variables can be accessed throughout the module by all functions.
%% Cell type:code id: tags:
``` python
# global variables
result = 0
multiplicator = 2
def multiply(arg0):
# here we create a new name `result` in the function namespace
# `result` is a local variable
# we can use the global variable `multiplicator`
result = multiplicator * arg0
print('Inside the function local result:\t', result)
return result
multiply(10)
print('Outside the function global result:\t', result)
```
%% Output
Inside the function local result: 20
Outside the function global result: 0
%% Cell type:markdown id: tags:
- Global variables can be used in a function.
- Global variables can not be modified in a function (except with the `global` keyword. Discourage!).
%% Cell type:markdown id: tags:
## Global vs Local variables: `global` keyword
There is a keyword `global` to define inside a function a global variable and to modify a global variable in the function. **It is often a bad idea to use it :-)**
%% Cell type:code id: tags:
``` python
def func():
global me
# Defined locally but declared as global
me = 'global variable locally defined'
print(me)
func()
# Ask for a global variable
print(me)
```
%% Output
global variable locally defined
global variable locally defined
%% Cell type:code id: tags:
``` python
delta = 0
def add_1_to_delta():
global delta
# global variable modified in a function
delta += 1
for i in range(4):
add_1_to_delta()
print(delta, end=', ')
```
%% Output
1, 2, 3, 4,
%% Cell type:markdown id: tags:
## Function Arguments
You can call a function by using the following types of formal arguments:
- Required arguments
- Keyword arguments
- Default arguments
- Variable-length arguments
%% Cell type:markdown id: tags:
### Required arguments
Required arguments are the arguments passed to a function in correct positional order. Here, the number of arguments in the function call should match exactly with the function definition.
%% Cell type:code id: tags:
``` python
def myfunc(arg0, arg1):
"Return the sum of the first argument with twice the second one."
result = arg0 + 2*arg1
print(f'arg0 + 2*arg1 = {arg0} + 2*{arg1} = {result}')
return result
myfunc(4, 6)
```
%% Output
arg0 + 2*arg1 = 4 + 2*6 = 16
16
%% Cell type:markdown id: tags:
To call the function `myfunc`, you definitely need to pass two arguments, otherwise it gives a syntax error.
%% Cell type:markdown id: tags:
### Keyword arguments
Keyword arguments are related to the function calls. When you use keyword arguments in a function call, Python identifies the arguments by the parameter name.
%% Cell type:code id: tags:
``` python
myfunc(arg1=3, arg0=2)
```
%% Output
arg0 + 2*arg1 = 2 + 2*3 = 8
8
%% Cell type:markdown id: tags:
### Default arguments
A default argument is an argument that assumes a default value if a value is not provided in the function call for that argument.
%% Cell type:code id: tags:
``` python
def myfunc1(arg0, arg1=None):
"Return the sum of the first argument with twice the second one."
if arg1 is None:
if type(arg0) == int:
arg1 = 0
else:
arg1 = ""
myfunc(arg0, arg1)
myfunc1("a", "n")
```
%% Output
arg0 + 2*arg1 = a + 2*n = ann
%% Cell type:code id: tags:
``` python
myfunc1(1, 3)
```
%% Output
arg0 + 2*arg1 = 1 + 2*3 = 7
%% Cell type:markdown id: tags:
### Default arguments
**Warning:** the default arguments are created only once, at the function definition! They are stored in a tuple associated with the function object.
%% Cell type:code id: tags:
``` python
def do_not_use_mutable_object_for_default_arg(l=[]):
l.append(1)
print(l)
```
%% Cell type:markdown id: tags:
**Exercice:** what will be the result of 3 calls of this function? Use a namespaces-objects diagram!
%% Cell type:markdown id: tags:
### Default arguments
**Warning:** the default arguments are created only once, at the function definition! They are stored in a tuple associated with the function object.
%% Cell type:code id: tags:
``` python
def do_not_use_mutable_object_for_default_arg(l=[]):
l.append(1)
print(l)
```
%% Cell type:code id: tags:
``` python
do_not_use_mutable_object_for_default_arg()
do_not_use_mutable_object_for_default_arg()
do_not_use_mutable_object_for_default_arg()
```
%% Output
[1]
[1, 1]
[1, 1, 1]
%% Cell type:code id: tags:
``` python
def how_to_use_list_as_default_arg(l=None):
if l is None:
l = []
l.append(1)
print(l)
how_to_use_list_as_default_arg()
how_to_use_list_as_default_arg()
how_to_use_list_as_default_arg()
l1 = [1,2,3]
how_to_use_list_as_default_arg(l1)
l1
```
%% Output
[1]
[1]
[1]
[1, 2, 3, 1]
[1, 2, 3, 1]
%% Cell type:markdown id: tags:
### Variable-length arguments
You may need to process a function for more arguments than you specified while defining the function. These arguments are called variable-length arguments and are not named in the function definition, unlike required and default arguments.
An asterisk (*) is placed before the variable name that holds the values of all nonkeyword variable arguments.
%% Cell type:code id: tags:
``` python
def sum_args(*args):
"""Return the sum of numbers."""
totalsum = 0
print('args =', args)
for var in args:
totalsum += var
print('totalsum =', totalsum)
return totalsum
sum_args()
sum_args(4)
sum_args(4, 3, 4, 7)
```
%% Output
args = ()
totalsum = 0
args = (4,)
totalsum = 4
args = (4, 3, 4, 7)
totalsum = 18
18
%% Cell type:markdown id: tags:
### Variable-length arguments
There is also a (very useful) syntax with two asterisks `**`, which works like this:
%% Cell type:code id: tags:
``` python
def func(a, b, *args, **kwargs):
print(f'call:\n\ta = {a}\n\targs = {args}\n\tkwargs = {kwargs}')
func(1, 2, 3, toto=3, titi=3)
func('a', 'b', bob=3)
```
%% Output
call:
a = 1
args = (3,)
kwargs = {'toto': 3, 'titi': 3}
call:
a = a
args = ()
kwargs = {'bob': 3}
%% Cell type:markdown id: tags:
### Do it yourself
Write a function that takes as input a list `l` and a number `a` and that multiplies
all the elements of the list by the number. If not set, number is defaulted to 2.
%% Cell type:markdown id: tags:
#### A possible solution:
%% Cell type:code id: tags:
``` python
def func(l, a=2):
for i, val in enumerate(l):
l[i] = a * val
l = list(range(3))
print(l)
func(l)
print(l)
func(l, 4)
print(l)
l = ['a', 'b']
func(l, 4)
print(l)
```
%% Output
[0, 1, 2]
[0, 2, 4]
[0, 8, 16]
['aaaa', 'bbbb']
%% Cell type:markdown id: tags:
## `lambda` keyword and anonymous functions
These functions are called anonymous because they are not declared by using the `def` keyword but with the `lambda` keyword so they have not name.
- Lambda forms can take any number of arguments but return just one value in the form of an expression. They cannot contain commands or multiple expressions.
- Lambda form is used in functional programming (but it is usually better to use list comprehension) and for [callbacks in GUI](https://pythonconquerstheuniverse.wordpress.com/2011/08/29/lambda_tutorial/).
%% Cell type:code id: tags:
``` python
f = lambda x, y: x + y
f(1, 2)
```
%% Output
3
%% Cell type:markdown id: tags:
## Example of the builtin function `print`
```text
In [1]: print?
Docstring:
print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)
Prints the values to a stream, or to sys.stdout by default.
Optional keyword arguments:
file: a file-like object (stream); defaults to the current sys.stdout.
sep: string inserted between values, default a space.
end: string appended after the last value, default a newline.
flush: whether to forcibly flush the stream.
Type: builtin_function_or_method
```
%% Cell type:code id: tags:
``` python
print('hello', 'Eric')
```
%% Output
hello Eric
%% Cell type:code id: tags:
``` python
print('hello', 'Eric', sep='_', end='')
print('.')
```
%% Output
hello_Eric.
%% Cell type:markdown id: tags:
## Input from Keyboard (builtin function `input`)
%% Cell type:code id: tags:
``` python
answer = input("what's your name ?")
print("your name is ", answer)
```
%% Output
what's your name ? eric
your name is eric
......
%% Cell type:markdown id: tags:
# Python training UGA 2017