Commit da3bcb90 authored by Loic Huder's avatar Loic Huder
Browse files
parents e2a41778 8a9d9eb6
......@@ -5,6 +5,7 @@
*.so
*.o
*.pstats
*.mod
_build
build
......@@ -24,4 +25,6 @@ ipynb/index.rst
ipynb/index.html
pyfiles/dtw_cort_dist/V5_cython/*.c
pyfiles/dtw_cort_dist/V5_cython/*.html
\ No newline at end of file
pyfiles/dtw_cort_dist/V5_cython/*.html
**/V*/res_cort.npy
**/V*/res_dtw.npy
\ No newline at end of file
......@@ -169,7 +169,7 @@ Please tell us before the training if it does not work.
#### Install few packages in your base conda environment
```
conda install ipython spyder jupyter numpy scipy pandas matplotlib
conda install --file requirements.txt
```
#### Check your environment
......
......@@ -12,7 +12,7 @@
"\n",
"**A training to scale up Python code **\n",
"\n",
"Pierre Augier (LEGI), Cyrille Bonamy (LEGI), Eric Maldonado (Irstea), Franck Thollard (ISTerre), Christophe Picard (LJK), Loïc Huder (ISTerre)"
"Pierre Augier (LEGI), Cyrille Bonamy (LEGI), Eric Maldonado (Irstea), Franck Thollard (ISTerre), Raphaël Bacher (GIPSA-LAB), Loïc Huder (ISTerre)"
]
},
{
......
%% Cell type:markdown id: tags:
# Python for High Performance Computing
**A training to scale up Python code **
Pierre Augier (LEGI), Cyrille Bonamy (LEGI), Eric Maldonado (Irstea), Franck Thollard (ISTerre), Christophe Picard (LJK), Loïc Huder (ISTerre)
Pierre Augier (LEGI), Cyrille Bonamy (LEGI), Eric Maldonado (Irstea), Franck Thollard (ISTerre), Raphaël Bacher (GIPSA-LAB), Loïc Huder (ISTerre)
%% Cell type:markdown id: tags:
# Python for High Performance Computing
- Day 0: Python language
- Day 1: Numpy / Scipy / Pandas + Visualization
- Day 2: Packaging, documentation, intro HPC, profiling
- Day 3 and 4: Wrapping, Python accelerators, parallel computing + personal projects
%% Cell type:markdown id: tags:
https://python.univ-grenoble-alpes.fr/
python@univ-grenoble-alpes.fr
<div><img style="float: left;margin-left : 70px" src='fig/logo_maimosine.png' height="100" width="100"/><img style="float: left; margin-left : 10px" src='fig/logo_sari.png' height="80" width="80"/><img style="float: left; margin-left : 10px" src='fig/logo_gricad.png' height="80" width="80"/> <img style="float: left; margin-left : 10px" src='fig/logo_UGA.png' height="80" width="80"/> <img style="float: left; margin-left : 10px" src='fig/logo_devlog.png' height="80" width="80"/> <img style="float: left; margin-left : 10px" src='fig/logo_CNRS.jpg' height="80" width="80"/> <img style="float: left; margin-left : 10px" src='fig/logo_data_institute.jpg' height="140" width="140"/></div>
%% Cell type:markdown id: tags:
# Formations
## Python Initiation
- début mars : formation initiation CNRS/UGA
- début avril : formation initiation Collège Ecole Doctorale/MaiMoSiNE
## Python calcul
- 12,13,17,18,19 Juin
@Doctorants - Inscription : https://www.adum.fr/script/formations.pl?mod=217234&site=UDG
@autres - Inscription : https://www.maimosine.fr/animation-scientifique/article/s-inscrire-a-une-formation
## Séminaire Machine Learning, IA, Deep Learning
- 6 juin 9h Auditorium IMAG
- Scikit-Learn, Keras entre autres
## Conf
- GeoPython 24 - 26 juin, Suisse
- Euroscipy 2019 takes place 2-6 September 2019 in Bilbao, Spain
https://python.univ-grenoble-alpes.fr/
(Pierre Augier - Franck Thollard - Raphaël Bacher - Cyrille Bonamy - Loic Huder - Eric Maldonado )
<div><img style="float: left;margin-left : 70px" src='fig/logo_maimosine.png' height="100" width="100"/><img style="float: left; margin-left : 10px" src='fig/logo_sari.png' height="80" width="80"/><img style="float: left; margin-left : 10px" src='fig/logo_gricad.png' height="80" width="80"/> <img style="float: left; margin-left : 10px" src='fig/logo_UGA.png' height="80" width="80"/> <img style="float: left; margin-left : 10px" src='fig/logo_devlog.png' height="80" width="80"/> <img style="float: left; margin-left : 10px" src='fig/logo_CNRS.jpg' height="80" width="80"/> <img style="float: left; margin-left : 10px" src='fig/logo_data_institute.jpg' height="140" width="140"/></div>
%% Cell type:markdown id: tags:
# Working sessions
<span style="color:red">Prochaine Session 20 Juin 10h @IMAG</span>
Présentation du moteur de workflow snakemake sur les serveurs de calcul du mesocentre CIMENT/GRICAD. (Florent Chuffard)
- Numpy, Scipy (mini-eigen) - Jupyter(lab)
- Matplotlib, other plotting tools: plotly, bokeh, altair,...
- GIS and maps (cartopy, basemap, ...)
- Sympy, sage - xarray, dask - Communication with machines / instruments, IoT
- Pandas, statmodels, bayesian stuff (Pymc, Emcee,...) - Machine learning: scikit-learn,...
- Deep learning: tensorflow (keras), pytorch,...
- Scikit-image, imageio, pillow, tifffile, opencv - HPC: mpi4py, h5py, h5netcdf, FFT, clusters, GPU
- Performance: cython, numba, pythran, swig
- GUI: PyQt, kivy, tk - Tests - Database - Package, deployment tool
- IDE: spyder, pycharm, eclipse(pydev), vim - Retour d'expériences de bascule sous Python
<span style="color:red">Recherche contributeurs également !</span>
<div><img style="float: left;margin-left : 70px" src='fig/logo_maimosine.png' height="100" width="100"/><img style="float: left; margin-left : 10px" src='fig/logo_sari.png' height="80" width="80"/><img style="float: left; margin-left : 10px" src='fig/logo_gricad.png' height="80" width="80"/> <img style="float: left; margin-left : 10px" src='fig/logo_UGA.png' height="80" width="80"/> <img style="float: left; margin-left : 10px" src='fig/logo_devlog.png' height="80" width="80"/> <img style="float: left; margin-left : 10px" src='fig/logo_CNRS.jpg' height="80" width="80"/> <img style="float: left; margin-left : 10px" src='fig/logo_data_institute.jpg' height="140" width="140"/></div>
......
This diff is collapsed.
This diff is collapsed.
......@@ -16,6 +16,8 @@
"\n",
"# Object-oriented programming: inheritance\n",
"\n",
"See https://docs.python.org/3/tutorial/classes.html\n",
"\n",
"Python is also a Object-oriented language. Object-oriented programming is very useful and used in many libraries so it is very useful to understand how the simple Object-oriented mechanisms work in Python."
]
},
......@@ -112,7 +114,12 @@
"source": [
"The first line states that instances of the class `AdultBee` will be Python objects. The class `AdultBee` inherits from the class `object`.\n",
"\n",
"The first line could have been replaced by the less explicit `class AdultBee:`. Actually, in Python 3, the classes that do not inherit explicitly from another class automatically inherit from the class `object`.\n"
"The first line could have been replaced by the less explicit `class AdultBee:`. Actually, in Python 3, the classes that do not inherit explicitly from another class automatically inherit from the class `object`.\n",
"\n",
"- `kind` and `limit_age` are **class variables**,\n",
"- `mother`, `father`, `tag` and `living` are **instance variables**,\n",
"- `__init__` is a **\"special\" method**,\n",
"- `act_and_envolve` and `die` are **methods**.\n"
]
},
{
......@@ -205,8 +212,8 @@
}
},
"source": [
"### Warning for C++ users\n",
"`__init__` is NOT the constructor. The real constructor is `__new__`. This method is called to really create the Python object and it really returns the object. Usually, we do not need to redefine it. Python `__init__` and C++ constructor have to be used in very different ways. Only the `__init__` method of the class is automatically called by Python during the instantiation. Nothing like the Russian dolls C++ mechanism."
"### Warning for C++ developers\n",
"`__init__` is NOT the constructor. The real constructor is `__new__`. This method is called to really create the Python object and it really returns the object. Usually, we do not need to redefine it. Python `__init__` and C++ constructor have to be used in very different ways. Only the `__init__` method of the class is automatically called by Python during the instantiation. Nothing like the Russian dolls C++ mechanism. All methods in Python are effectively virtual.\n"
]
},
{
......@@ -280,7 +287,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"To indicate the dependency to an other class, we put the parent class in parenthesis in the definition. The class `QueenBee` inherits from the class `AdultBee`"
"To indicate the dependency to another class, we put the parent class in parenthesis in the definition. The class `QueenBee` inherits from the class `AdultBee`"
]
},
{
......@@ -466,7 +473,7 @@
},
{
"cell_type": "code",
"execution_count": 12,
"execution_count": 10,
"metadata": {},
"outputs": [
{
......@@ -484,6 +491,7 @@
"\n",
"class Student(Person):\n",
" role = 'student'\n",
"\n",
" @classmethod\n",
" def show_role(cls):\n",
" print('The role for this class is ' +\n",
......@@ -500,14 +508,14 @@
}
},
"source": [
"### \"Static methods\"\n",
"### \"Static methods\" and class variables\n",
"\n",
"For some situation we don't even need to explicitly use the class or an instance. We can use static methods."
]
},
{
"cell_type": "code",
"execution_count": 13,
"execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
......@@ -525,7 +533,7 @@
},
{
"cell_type": "code",
"execution_count": 14,
"execution_count": 12,
"metadata": {},
"outputs": [
{
......@@ -537,297 +545,13 @@
}
],
"source": [
"p1 = IdPerson('Pierre')\n",
"p1 = IdPerson('Maya')\n",
"p2 = IdPerson('Cyrille')\n",
"p3 = IdPerson('Olivier')\n",
"p4 = IdPerson('Franck')\n",
"\n",
"IdPerson.show_nb_person()"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Do it yourself\n",
"At the end of the last presentation, we asked the following question about our weather stations measuring wind and temperature:\n",
"> What if we now have a weather station that also measure humidity ? Do we have to rewrite everything ?\n",
"\n",
"Give your own answer by doing the following tasks:\n",
"- Write a class `HumidWeatherStation` inheriting `WeatherStation` (code reproduced below) to implement a new attribute to store the humidity measurements.\n",
"- Write a function `humidity_at_max_temp` that returns the value of the humidity at the maximal temperature. Use the fact that `HumidWeatherStation` inherits from `WeatherStation` and therefore can use the method `arg_max_temp` previously implemented !\n",
"- *Advanced*: Overloadg the methods of `WeatherStation` to take humidity into account when computing percieved temperatures `Tp`. For simplicity, we will assume that `Tp = Tw + 5*humidity` with `Tw` the temperature computed with the wind chill effect.\n",
"- *Advanced*: Write tests for this new class"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [],
"source": [
"# Code to use for the DIY\n",
"class WeatherStation(object):\n",
" \"\"\" A weather station that holds wind and temperature \"\"\"\n",
"\n",
" def __init__(self, wind, temperature):\n",
" \"\"\" initialize the weather station. \n",
" Precondition: wind and temperature must have the same length\n",
" :param wind: any ordered iterable\n",
" :param temperature: any ordered iterable\"\"\"\n",
" self.wind = [x for x in wind]\n",
" self.temp = [x for x in temperature]\n",
" if len(self.wind) != len(self.temp):\n",
" raise ValueError(\n",
" \"wind and temperature should have the same size\"\n",
" )\n",
"\n",
" def perceived_temp(self, index):\n",
" \"\"\" computes the perceived temp according to \n",
" https://en.wikipedia.org/wiki/Wind_chill\n",
" i.e. The standard Wind Chill formula for Environment Canada is: \n",
" apparent = 13.12 + 0.6215*air_temp - 11.37*wind_speed^0.16 + 0.3965*air_temp*wind_speed^0.16\n",
" \n",
" :param index: the index for which the computation must be made\n",
" :return: the perceived temperature\"\"\"\n",
" air_temp = self.temp[index]\n",
" wind_speed = self.wind[index]\n",
" # Perceived temperature does not have a sense without wind...\n",
" if wind_speed == 0:\n",
" apparent_temp = air_temp\n",
" else:\n",
" apparent_temp = 13.12 + 0.6215*air_temp \\\n",
" - 11.37*wind_speed**0.16 \\\n",
" + 0.3965*air_temp*wind_speed**0.16\n",
" # Let's round to the integer to avoid trailing decimals...\n",
" return round(apparent_temp,0)\n",
" \n",
" def perceived_temperatures(self):\n",
" \"\"\" Returns an array of percieved temp computed from the temperatures and wind speed data \"\"\"\n",
" apparent_temps = []\n",
" for index in range(len(self.wind)):\n",
" # Reusing the method perceived_temp defined above\n",
" apparent_temperature = self.perceived_temp(index)\n",
" apparent_temps.append(apparent_temperature)\n",
" return apparent_temps\n",
"\n",
" def max_temp(self, perceived=False):\n",
" \"\"\" returns the maximum temperature record in the station\"\"\"\n",
" if perceived:\n",
" apparent_temp = self.perceived_temperatures()\n",
" return max(apparent_temp)\n",
" else:\n",
" return max(self.temp)\n",
"\n",
" def arg_max_temp(self, perceived=False):\n",
" \"\"\" returns the index of (one of the) maximum temperature record in the station\"\"\"\n",
" if perceived:\n",
" temp_array_to_search = self.perceived_temperatures()\n",
" else:\n",
" temp_array_to_search = self.temp\n",
" return temp_array_to_search.index(self.max_temp(perceived))"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"### A Solution"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0.5\n",
"35\n",
"[33.9, 39.15, 37.05, 37.9, 41.5, 41.6]\n",
"41.6\n"
]
}
],
"source": [
"class HumidWeatherStation(WeatherStation):\n",
" \"\"\" A weather station that holds wind, temperature and humidity. Inherits from WeatherStation \"\"\"\n",
" \n",
" def __init__(self, wind, temperature, humidity):\n",
" \"\"\" initialize the weather station. \n",
" Precondition: wind, temperature and humidity must have the same length\n",
" :param wind: any ordered iterable\n",
" :param temperature: any ordered iterable\n",
" :param humidity: any ordered iterable\"\"\"\n",
" # Delegate the initialisation of wind and temperature to the mother class constructor\n",
" super(HumidWeatherStation, self).__init__(wind, temperature)\n",
" # Or: super().init(wind, temperature)\n",
" \n",
" # Add humidity treatement\n",
" self.humidity = [x for x in humidity]\n",
" if len(self.humidity) != len(self.temp):\n",
" raise ValueError(\"humidity and temperature should have the same size\")\n",
" # If humidity and temp have the same size, humidity and wind do as well\n",
" # as len(temp) == len(wind) is enforced from the mother class constructor\n",
" \n",
" def humidity_at_max_temp(self):\n",
" \"\"\" Returns the value of humidity at the maximal temperature \n",
" \"\"\"\n",
" index_max_temp = self.arg_max_temp()\n",
" return self.humidity[index_max_temp]\n",
" \n",
" def perceived_temp(self, index):\n",
" \"\"\" Compute the perceived temperature according to wind_speed (wind-chill) and humidity \n",
" \n",
" :param index: the index for which the computation must be made\n",
" :return: the perceived temperature\"\"\"\n",
" # Compute the wind-chilled temp from WeatherStation method\n",
" wind_chilled_temp = super().perceived_temp(index)\n",
" apparent_temp = wind_chilled_temp + 5*self.humidity[index]\n",
" return round(apparent_temp, 2)\n",
" \n",
" def perceived_temps(self):\n",
" \"\"\" Returns an array of percieved temp computed from the temperatures, wind speed and humidity data \"\"\"\n",
" apparent_temps = []\n",
" for index in range(len(self.temp)):\n",
" # This time, we use the perceived_temp method of HumidWeatherStation\n",
" apparent_temperature = self.perceived_temp(index)\n",
" apparent_temps.append(apparent_temperature)\n",
" return apparent_temps\n",
" \n",
"singapore = HumidWeatherStation(wind=[11, 23, 23, 19, 18, 18], \n",
" temperature = [28, 33, 31, 32, 35, 34],\n",
" humidity = [0.78, 0.63, 0.61, 0.58, 0.5, 0.72])\n",
"print(singapore.humidity_at_max_temp()) #0.5 expected\n",
"print(singapore.max_temp()) #35 expected\n",
"# As we overloaded perceived_temp, the rest of the class features work with the new behaviour\n",
"print(singapore.perceived_temps())\n",
"print(singapore.max_temp(perceived=True))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In this case, we used inheritance to create the new object (`HumidWeatherStation`) to:\n",
"\n",
"- Add new features (`humidity_at_max_temp`) to an existing object without rewritting the common features\n",
"- Define new behaviours for features already present (`perceived_temp`) that integrate well with the structure in place\n",
"\n",
"For such needs, think about inheritance."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Do it yourself (advanced)\n",
"\n",
"- Write a class named `MovingObject` that has at least one attribute `position` and implements two functions `start()` and `stop()`. These 2 functions could just print for example \"starting\" and \"stoping\" (or they could do more funny things)...\n",
"\n",
"- Write another class named `Car` that inherits `MovingObject` and overload start and stop functions.\n",
"\n",
"- Use the classes (instantiate objects and use them).\n",
"\n",
"- Options : add a static method in Car class that returns the number of cars."
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The car Ferrari starts (vrooum)\n",
"The car Ferrari stops\n",
"The car Porsche starts (vrooum)\n",
"The car Porsche stops\n",
"The car Ferrari starts (vrooum)\n",
"The car Porsche starts (vrooum)\n",
"starting\n",
"pollution at the end: 2.6\n"
]
}
],
"source": [
"# a solution\n",
"pollution = 0.\n",
"\n",
"class MovingObject:\n",
" \n",
" def __init__(self, position=0., max_speed=1., acceleration=1.):\n",
" self.position = position\n",
" self.max_speed = max_speed\n",
" self.acceleration = acceleration\n",
" \n",
" def start(self):\n",
" print (\"starting\")\n",
"\n",
" def stop(self):\n",
" print (\"stoping\")\n",
"\n",
"\n",
"class Car(MovingObject):\n",
" count = 0\n",
"\n",
" def __init__(self, position=0., max_speed=1., acceleration=1., name='', pollution_start=0.5):\n",
" super(Car, self).__init__(position, max_speed, acceleration)\n",
" self.name = name\n",
" self.pollution_start = pollution_start\n",
" Car.count += 1\n",
"\n",
" def start(self):\n",
" global pollution\n",
" print ('The car ' + self.name + ' starts (vrooum)')\n",
" pollution += self.pollution_start\n",
"\n",
" def stop(self):\n",
" print ('The car ' + self.name + ' stops') \n",
" \n",
" @staticmethod\n",
" def get_number_of_cars():\n",
" print (\"There are \" + str(Car.count) + \" cars\")\n",
"\n",
"class Bike(MovingObject):\n",
" pass\n",
" \n",
"ferrari = Car(name='Ferrari')\n",
"mybike = Bike()\n",
"mybike.name = 'blue bike'\n",
"porsche = Car(name='Porsche', pollution_start=0.8)\n",
"\n",
"ferrari.start()\n",
"ferrari.stop()\n",
"porsche.start()\n",
"porsche.stop()\n",
"\n",
"objs = [ferrari, porsche, mybike]\n",
"for f in objs:\n",
" f.start()\n",
" \n",
"print(f'pollution at the end: {pollution}')"
]
}
],
"metadata": {
......@@ -847,7 +571,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.2"
"version": "3.7.3"
}
},
"nbformat": 4,
......
%% Cell type:markdown id: tags:
# Python training UGA 2017
**A training to 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)
# Object-oriented programming: inheritance
See https://docs.python.org/3/tutorial/classes.html
Python is also a Object-oriented language. Object-oriented programming is very useful and used in many libraries so it is very useful to understand how the simple Object-oriented mechanisms work in Python.
%% Cell type:markdown id: tags:
For some problems, Object-oriented programming is a very efficient paradigm. Many libraries use it so it is necessary to understand how it works in Python to really use these libraries.
# Concepts
## Object
An object is an entity that has state and behavior. Objects are the basic elements of object oriented system.
## Class
Classes are "families" of objects. A class describes how are organized its objects and how they work.
%% Cell type:markdown id: tags:
## Example of problem: Simulate populations of honeybees
<img src="https://static.independent.co.uk/s3fs-public/styles/story_large/public/thumbnails/image/2013/06/05/18/web-bees-epa.jpg" title="bee" alt="image bee" width="30%">
#### Hierarchy of honeybees
The "adult" bees can be:
- queen
- workers
- fertile males
Each type of adult bee have different characteristics, behaviors, activities and tasks.
%% Cell type:markdown id: tags:
## Class definition
A class is a logical entity which contains attributes and have some behavior. When a class is defined, we need to specify its name, its dependencies (the class inherits from at least one other class), its attributes and its methods.
%% Cell type:code id: tags:
``` python
class AdultBee(object):
kind = None
limit_age = 50.
def __init__(self, mother, father, tag=None):
self.mother = mother
self.father = father
if tag is None:
self.tag = (self.mother.tag, self.father.tag)
else:
self.tag = tag
# age in days
self.age = 0.
self.living = True
def act_and_envolve(self, duration=1):
"""Time stepping method"""
self.age += duration
if self.age > self.limit_age:
self.die()
def die(self):
self.living = False
```
%% Cell type:markdown id: tags:
The first line states that instances of the class `AdultBee` will be Python objects. The class `AdultBee` inherits from the class `object`.
The first line could have been replaced by the less explicit `class AdultBee:`. Actually, in Python 3, the classes that do not inherit explicitly from another class automatically inherit from the class `object`.
- `kind` and `limit_age` are **class variables**,
- `mother`, `father`, `tag` and `living` are **instance variables**,
- `__init__` is a **"special" method**,
- `act_and_envolve` and `die` are **methods**.
%% Cell type:markdown id: tags:
### Instantiation of a class
We can create objects `AdultBee`. We say that we *instantiate objects of the class `AdultBee`*.
%% Cell type:code id: tags:
``` python
bee0 = AdultBee('mother0', 'father0', tag='0')
bee1 = AdultBee('mother1', 'father1', tag='1')
bee_second_generation0 = AdultBee(bee0, bee1)
bee_second_generation1 = AdultBee(bee0, bee1)
bee_third_generation = AdultBee(
bee_second_generation0, bee_second_generation1)
bees = [bee0, bee1, bee_second_generation0, bee_second_generation1, bee_third_generation]
```
%% Cell type:markdown id: tags:
In this example, we manipulate the notions of class, object (instance), abstraction and encapsulation...
#### Syntax to create an object
%% Cell type:code id: tags:
``` python
bee2 = AdultBee('mother2', 'father2', tag='2')
```
%% Cell type:markdown id: tags:
#### What happens...
- the Python object is first created
- the object is initialized, i.e. the method `__init__` is automatically called like this (for `bee0`):
```python
AdultBee.__init__(bee0, 'mother0', 'father0', tag='0')
```
%% Cell type:markdown id: tags:
### Special methods and attributes
In Python, methods or attributes that starts with `__` are "special". Such methods and attributes are used internally by Python. They define how the class works internally.
For example the method `__init__` is automatically called by Python during the instantiation of an object with the arguments used for the instantiation.
### Protected methods and attributes (no notion of public, private, virtual as in C++)
Attributes and methods whose names start with `_` are said to be "protected". It is just a name convention. It tells the users that they should not use these objects directly.
%% Cell type:markdown id: tags:
### Warning for C++ users
`__init__` is NOT the constructor. The real constructor is `__new__`. This method is called to really create the Python object and it really returns the object. Usually, we do not need to redefine it. Python `__init__` and C++ constructor have to be used in very different ways. Only the `__init__` method of the class is automatically called by Python during the instantiation. Nothing like the Russian dolls C++ mechanism.
### Warning for C++ developers
`__init__` is NOT the constructor. The real constructor is `__new__`. This method is called to really create the Python object and it really returns the object. Usually, we do not need to redefine it. Python `__init__` and C++ constructor have to be used in very different ways. Only the `__init__` method of the class is automatically called by Python during the instantiation. Nothing like the Russian dolls C++ mechanism. All methods in Python are effectively virtual.
%% Cell type:markdown id: tags:
### Use the objects (instances)
%% Cell type:code id: tags:
``` python
print('second generation:', bee_second_generation0.tag)
print('third generation; ', bee_third_generation.tag)
print('warning: consanguinity...')
```
%%%% Output: stream
second generation: ('0', '1')
third generation; (('0', '1'), ('0', '1'))
warning: consanguinity...
%% Cell type:code id: tags:
``` python
# 100 days
for i in range(100):
for bee in bees:
bee.act_and_envolve()
bees = [bee for bee in bees if bee.living]
if len(bees) == 0:
print('After 100 days, no more bees :-(')
```
%%%% Output: stream
After 100 days, no more bees :-(
%% Cell type:markdown id: tags:
## Inheritance
%% Cell type:markdown id: tags:
To indicate the dependency to an other class, we put the parent class in parenthesis in the definition. The class `QueenBee` inherits from the class `AdultBee`
To indicate the dependency to another class, we put the parent class in parenthesis in the definition. The class `QueenBee` inherits from the class `AdultBee`
%% Cell type:code id: tags:
``` python
class QueenBee(AdultBee):
kind = 'queen'
limit_age = 4*365
def act_and_envolve(self, duration=1):
"""Time stepping method"""
super().act_and_envolve(duration)
print('I am the Queen!')
class WorkerBee(AdultBee):
kind = 'worker'
# actually it depends on the season...
limit_age = 6*7
def dance(self):
print('I communicate by dancing')
def make_honey(self):
print('I make honey')
```
%% Cell type:markdown id: tags:
- The methods that are not rewritten are automatically inherited from the parent class.
- The methods that are rewritten are completely replaced. To use the method of the parent class, it has to be called explicitly (for example with the `super()` function).
We see that we do not need to rewrite all methods. For example the method `__init__` of the class `QueenBee` is the method `__init__` of the class `AdultBee`.
%% Cell type:markdown id: tags:
The class `AdultBee` that we defined is also derived from a more generic class that is called `object`. Let's check the content of the class `QueenBee`.
%% Cell type:code id: tags:
``` python
queen = QueenBee('mother0', 'father0', tag='0')
print(dir(queen))
```
%%%% Output: stream
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'act_and_envolve', 'age', 'die', 'father', 'kind', 'limit_age', 'living', 'mother', 'tag']
%% Cell type:markdown id: tags:
All the methods that star with the prefix `__` are inherited from the class `object`. All classes in Python3 inherit from `object`.
%% Cell type:code id: tags:
``` python
queen.act_and_envolve()
```
%%%% Output: stream
I am the Queen!
%% Cell type:markdown id: tags:
### `super` function
We have used the function `super()` like this to call a function of the parent class:
```python
super().act_and_envolve(duration)
```
Remark: the python 2 syntax was more complicated. We would have to write:
```python
super(QueenBee, self).act_and_envolve(duration)
```
Remark: we can also call the method explicitly:
```
AdultBee.act_and_envolve(self, duration)
```
%% Cell type:markdown id: tags:
## Remark: the exceptions are classes...
We can define our own exceptions classes inheriting from an exception class.
%% Cell type:code id: tags:
``` python
class MyValueError(ValueError):
pass
def myfunc():
raise MyValueError('oops')
try:
myfunc()
except OSError:
print('An OSError was raised')
except ValueError:
print('A ValueError was raised')
```
%%%% Output: stream
A ValueError was raised
%% Cell type:markdown id: tags:
## Static methods and class methods (advanced)
%% Cell type:markdown id: tags:
### "Class methods"
When we simply define a method in a class, it is a instance method, i.e. the first argument of the method (`self`) points toward the instance used to call the method. This is the normal and most common mechanism.
We could also define methods that work for the class using the decorator `@classmethod`:
%% Cell type:code id: tags:
``` python
class Person(object):
def __init__(self):
pass
class Student(Person):
role = 'student'
@classmethod
def show_role(cls):
print('The role for this class is ' +
cls.role + '.')
Student.show_role()
```
%%%% Output: stream
The role for this class is student.
%% Cell type:markdown id: tags:
### "Static methods"
### "Static methods" and class variables
For some situation we don't even need to explicitly use the class or an instance. We can use static methods.
%% Cell type:code id: tags:
``` python
class IdPerson(Person):
count = 0
def __init__(self, name):
self.name = name
self.id = IdPerson.count
IdPerson.count += 1
@staticmethod
def show_nb_person():
print('Number of persons created: ', IdPerson.count)
```
%% Cell type:code id: tags:
``` python
p1 = IdPerson('Pierre')
p1 = IdPerson('Maya')
p2 = IdPerson('Cyrille')
p3 = IdPerson('Olivier')
p4 = IdPerson('Franck')
IdPerson.show_nb_person()
```
%%%% Output: stream
Number of persons created: 4
%% Cell type:markdown id: tags:
## Do it yourself
At the end of the last presentation, we asked the following question about our weather stations measuring wind and temperature:
> What if we now have a weather station that also measure humidity ? Do we have to rewrite everything ?
Give your own answer by doing the following tasks:
- Write a class `HumidWeatherStation` inheriting `WeatherStation` (code reproduced below) to implement a new attribute to store the humidity measurements.
- Write a function `humidity_at_max_temp` that returns the value of the humidity at the maximal temperature. Use the fact that `HumidWeatherStation` inherits from `WeatherStation` and therefore can use the method `arg_max_temp` previously implemented !
- *Advanced*: Overloadg the methods of `WeatherStation` to take humidity into account when computing percieved temperatures `Tp`. For simplicity, we will assume that `Tp = Tw + 5*humidity` with `Tw` the temperature computed with the wind chill effect.
- *Advanced*: Write tests for this new class
%% Cell type:code id: tags:
``` python
# Code to use for the DIY
class WeatherStation(object):
""" A weather station that holds wind and temperature """
def __init__(self, wind, temperature):
""" initialize the weather station.
Precondition: wind and temperature must have the same length
:param wind: any ordered iterable
:param temperature: any ordered iterable"""
self.wind = [x for x in wind]
self.temp = [x for x in temperature]
if len(self.wind) != len(self.temp):
raise ValueError(
"wind and temperature should have the same size"
)
def perceived_temp(self, index):
""" computes the perceived temp according to
https://en.wikipedia.org/wiki/Wind_chill
i.e. The standard Wind Chill formula for Environment Canada is:
apparent = 13.12 + 0.6215*air_temp - 11.37*wind_speed^0.16 + 0.3965*air_temp*wind_speed^0.16
:param index: the index for which the computation must be made
:return: the perceived temperature"""
air_temp = self.temp[index]
wind_speed = self.wind[index]
# Perceived temperature does not have a sense without wind...
if wind_speed == 0:
apparent_temp = air_temp
else:
apparent_temp = 13.12 + 0.6215*air_temp \
- 11.37*wind_speed**0.16 \
+ 0.3965*air_temp*wind_speed**0.16
# Let's round to the integer to avoid trailing decimals...
return round(apparent_temp,0)
def perceived_temperatures(self):
""" Returns an array of percieved temp computed from the temperatures and wind speed data """
apparent_temps = []
for index in range(len(self.wind)):
# Reusing the method perceived_temp defined above
apparent_temperature = self.perceived_temp(index)
apparent_temps.append(apparent_temperature)