Commit db264654 authored by Loic Huder's avatar Loic Huder
Browse files

Enhanced pres080: typo corrections and adding correction to the advanced DIY

parent 8e889031
......@@ -16,7 +16,7 @@
"\n",
"# Object-oriented programming: encapsulation\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."
"Python is also an 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."
]
},
{
......@@ -27,12 +27,12 @@
}
},
"source": [
"For some problems, Object-oriented programming is a very efficient paradigme. Many libraries use it so it is worth understanding what is object orriented programming and when it is usefull.\n",
"For some problems, Object-oriented programming is a very efficient paradigm. Many libraries use it so it is worth understanding what is object orriented programming and when it is useful.\n",
"\n",
"# Concepts\n",
"\n",
"## Object\n",
"An object is an entity that has state and behavior. Objects are the basic elements of object oriented system.\n",
"An object is an entity that has a state and a behavior. Objects are the basic elements of object-oriented system.\n",
"\n",
"## Class\n",
"Classes are \"families\" of objects. A class is a pattern that describes how objects will be built."
......@@ -50,20 +50,20 @@
"\n",
"**Example: the weather stations**\n",
"\n",
"Let us suppose we have a set of weather stations that does measurements of wind and temperature. Suppose now one wants to compute some statistics on these data. A basic representation of a station will be an array of arrays: win values and temperature values. "
"Let us suppose we have a set of weather stations that do measurements of wind speed and temperature. Suppose now one wants to compute some statistics on these data. A basic representation of a station will be an array of arrays: wind values and temperature values."
]
},
{
"cell_type": "code",
"execution_count": 1,
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"max temp is 5 at index 1 \n",
"wind_when_max_temp = 0\n"
"max temp is of 5°C at index 1 \n",
"wind at max temp = 0 km/h\n"
]
}
],
......@@ -72,8 +72,8 @@
"\n",
"# get wind when temperature is maximal\n",
"idx_max_temp = paris[1].index(max(paris[1]))\n",
"print(f\"max temp is {paris[1][idx_max_temp]} at index {idx_max_temp} \")\n",
"print(f\"wind_when_max_temp = {paris[0][idx_max_temp]}\")"
"print(f\"max temp is of {paris[1][idx_max_temp]}°C at index {idx_max_temp} \")\n",
"print(f\"wind speed at max temp = {paris[0][idx_max_temp]} km/h\")"
]
},
{
......@@ -88,8 +88,8 @@
"\n",
"Many problems:\n",
"\n",
"- if the number of measure type increases (e.g. having rainfall, humidity, ...) we will be lost with our indexing (what will data[5] will represent? wind, temperature, ..., ?)\n",
"- analysing what the code does is not (that) straightforward"
"- if the number of measurements increases (e.g. having rainfall, humidity, ...) the previous indexing will not be valid (what will paris[5] will represent? wind, temperature, ..., ?)\n",
"- Code analysis is not (that) straightforward"
]
},
{
......@@ -114,8 +114,8 @@
"name": "stdout",
"output_type": "stream",
"text": [
"max temp is 5 at index 1\n",
"wind_when_max_temp = 0\n"
"max temp is 5°C at index 1\n",
"wind_when_max_temp = 0 km/h\n"
]
}
],
......@@ -126,8 +126,8 @@
"paris_temp = paris[\"temperature\"]\n",
"idx_max_temp = paris_temp.index(max(paris_temp))\n",
"\n",
"print(f\"max temp is {paris_temp[idx_max_temp]} at index {idx_max_temp}\")\n",
"print(f\"wind_when_max_temp = {paris['wind'][idx_max_temp]}\")"
"print(f\"max temp is {paris_temp[idx_max_temp]}°C at index {idx_max_temp}\")\n",
"print(f\"wind speed at max temp = {paris['wind'][idx_max_temp]} km/h\")"
]
},
{
......@@ -140,11 +140,11 @@
"source": [
"**Comments**\n",
"- Pro\n",
" - More lisible code (reading paris[\"temperature\"] is more lisible than paris[1])\n",
" - Less error prone code (*i.e.* exchanging index for choosing the right array is very easy to do and very hard to debug)\n",
" - More readable code (reading paris[\"temperature\"] is clearer than paris[1])\n",
" - Less error prone code (*i.e.* using words as keys allow to not use index numbers that are easily mistaken and lead to code that is hard to read and debug)\n",
" \n",
"- Con \n",
" - Code for computing the final result is not very lisible"
" - The code to compute the final result is not very readable"
]
},
{
......@@ -191,13 +191,13 @@
"\n",
"idx_max_temp = arg_max_temp(paris)\n",
"\n",
"print(f\"max temp is {max_temp(paris)} at index {arg_max_temp(paris)}\")\n",
"print(f\"wind when max temp = {paris['wind'][idx_max_temp]}\")"
"print(f\"max temp is {max_temp(paris)}°C at index {arg_max_temp(paris)}\")\n",
"print(f\"wind speed at max temp = {paris['wind'][idx_max_temp]} km/h\")"
]
},
{
"cell_type": "code",
"execution_count": 4,
"execution_count": 6,
"metadata": {},
"outputs": [
{
......@@ -205,12 +205,12 @@
"output_type": "stream",
"text": [
"test_arg_max_temp (__main__.TestMyStuff0)\n",
"test arg max computs correctly ... ok\n",
"test arg_max_temp computes correctly ... ok\n",
"test_max_temp (__main__.TestMyStuff0)\n",
"test max computes correctly ... ok\n",
"test max_temp computes correctly ... ok\n",
"\n",
"----------------------------------------------------------------------\n",
"Ran 2 tests in 0.014s\n",
"Ran 2 tests in 0.003s\n",
"\n",
"OK\n"
]
......@@ -225,18 +225,18 @@
" \"\"\"Test the weather station \"\"\"\n",
"\n",
" def setUp(self):\n",
" \"\"\"Generates a well formed station (paris)\"\"\"\n",
" \"\"\"Generates a well structured station (paris)\"\"\"\n",
" self.paris = {\n",
" \"wind\": [10, 0, 20, 30, 20, 0],\n",
" \"temperature\": [1, 5, 1, -1, -1, 3],\n",
" }\n",
"\n",
" def test_max_temp(self):\n",
" \"\"\" test max computes correctly\"\"\"\n",
" \"\"\" test max_temp computes correctly\"\"\"\n",
" self.assertEqual(5, max_temp(self.paris))\n",
"\n",
" def test_arg_max_temp(self):\n",
" \"\"\" test arg max computs correctly\"\"\"\n",
" \"\"\" test arg_max_temp computes correctly\"\"\"\n",
" self.assertEqual(1, arg_max_temp(self.paris))\n",
"\n",
"\n",
......@@ -255,9 +255,9 @@
"**Comments**\n",
"\n",
"- Pro:\n",
" - adding functions leads to a more lisible code\n",
" - testing functions can be done separatelly to the rest of the code\n",
" - the computation done on the second part depends upons the functions (*i.e* they do what they claim)\n",
" - adding functions leads to a code that is easier to read (and therefore to debug!)\n",
" - testing functions can be done separately from the rest of the code\n",
" - the computation done on the second part depends upon the functions (*i.e* it depends on the function definitions)\n",
"- Con \n",
" - We rely on the fact that the dictionnaries have been built correctly (for example wind and temperature arrays have the same length)."
]
......@@ -272,20 +272,20 @@
"source": [
"**Improvement**\n",
"\n",
"Define a function that build the station (delegate the building of the station dictionnary to a function)"
"Define a function that builds the station (delegate the generation of the station dictionnary to a function)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"max temp is 5 at index 1\n",
"wind when max_temp = 0\n"
"max temp is 5°C at index 1\n",
"wind speed at max temp = 0 km/h\n"
]
}
],
......@@ -314,13 +314,13 @@
"paris = build_station([10, 0, 20, 30, 20, 0], [1, 5, 1, -1, -1, 3])\n",
"idx_max_temp = arg_max_temp(paris)\n",
"\n",
"print(f\"max temp is {max_temp(paris)} at index {arg_max_temp(paris)}\")\n",
"print(f\"wind when max_temp = {paris['wind'][idx_max_temp]}\")"
"print(f\"max temp is {max_temp(paris)}°C at index {arg_max_temp(paris)}\")\n",
"print(f\"wind speed at max temp = {paris['wind'][idx_max_temp]} km/h\")"
]
},
{
"cell_type": "code",
"execution_count": 6,
"execution_count": 7,
"metadata": {},
"outputs": [
{
......@@ -328,14 +328,16 @@
"output_type": "stream",
"text": [
"test_arg_max_temp (__main__.TestMyStuff1)\n",
"test arg max computs correctly ... ok\n",
"test_build_station_with_iterable (__main__.TestMyStuff1) ... ok\n",
"test arg_max_temp computs correctly ... ok\n",
"test_build_station_with_iterable (__main__.TestMyStuff1)\n",
"Tests that the station can be generated from iterables ... ok\n",
"test_max_temp (__main__.TestMyStuff1)\n",
"test max computes conrrectly ... ok\n",
"test_wrong_build (__main__.TestMyStuff1) ... ok\n",
"test max_temp computes conrrectly ... ok\n",
"test_wrong_build (__main__.TestMyStuff1)\n",
"Tests that the station generation throws an error ... ok\n",
"\n",
"----------------------------------------------------------------------\n",
"Ran 4 tests in 0.011s\n",
"Ran 4 tests in 0.005s\n",
"\n",
"OK\n"
]
......@@ -348,10 +350,11 @@
" \"\"\"Test the weather station \"\"\"\n",
"\n",
" def setUp(self):\n",
" \"\"\"Generates a well formed station (paris)\"\"\"\n",
" \"\"\"Generates a well structured station (paris)\"\"\"\n",
" self.paris = build_station([10, 0, 20, 30, 20, 0], [1, 5, 1, -1, -1, 3])\n",
"\n",
" def test_build_station_with_iterable(self):\n",
" \"\"\" Tests that the station can be generated from iterables \"\"\"\n",
" station = build_station(range(10), range(10))\n",
" self.assertEqual(station[\"wind\"][0], 0)\n",
" self.assertEqual(station[\"wind\"][-1], 9)\n",
......@@ -359,15 +362,17 @@
" self.assertEqual(station[\"temperature\"][-1], 9)\n",
"\n",
" def test_wrong_build(self):\n",
" \"\"\" Tests that the station generation throws an error \n",
" if wind and temperature do not have the same size\"\"\"\n",
" with self.assertRaises(ValueError):\n",
" bad = build_station(range(10), range(4))\n",
"\n",
" def test_max_temp(self):\n",
" \"\"\" test max computes conrrectly\"\"\"\n",
" \"\"\" test max_temp computes conrrectly\"\"\"\n",
" self.assertEqual(5, max_temp(self.paris))\n",
"\n",
" def test_arg_max_temp(self):\n",
" \"\"\" test arg max computs correctly\"\"\"\n",
" \"\"\" test arg_max_temp computs correctly\"\"\"\n",
" self.assertEqual(1, arg_max_temp(self.paris))\n",
"\n",
"\n",
......@@ -385,10 +390,10 @@
"source": [
"**Comments**\n",
"\n",
" - If people use build_station, then, the returned dictionary is well formed) \n",
" - If one changes build_station, only max_temp and arg_max_temp has to be changed accordingly\n",
" - We use a list comprehension to be able to have parameters wind and temp provided by any ordered iterable (e.g. range)\n",
" - If we have a new kind of station, *i.e.* that holds only wind and humidity, we wand to avoid to be able to use max_temp with it. "
" - If the dedicated function `build_station` is used, the returned dictionary is well structured.\n",
" - If one changes `build_station`, only `max_temp` and `arg_max_temp` have to be changed accordingly\n",
" - We use a list comprehension to be able to have parameters wind and temp provided by any ordered iterable (e.g. see `test_build_station_with_iterable` wtih `range`)\n",
" - *BUT* if we have a new kind of station, *i.e.* that holds only wind and humidity, we want to avoid to be able to use max_temp with it. "
]
},
{
......@@ -403,7 +408,7 @@
" \n",
"We would like to \"embed\" the max_temp and the arg_max_temp in the \"dictionnary station\" in order to address the last point.\n",
" \n",
"And here comes the Object programming ! \n",
"And here comes the object-oriented programming ! \n",
" "
]
},
......@@ -418,22 +423,23 @@
"## Object oriented in a nutshell\n",
"\n",
"A class defines a template used for building object. \n",
"In our running example, the class defines the specifications of what is a weather station (*i.e*, a weather station should contain an array for wind (named \"wind\" and an array for temperature, named \"temp\"), and one object is paris.\n",
"In our example, the class (named `WeatherStation`) defines the specifications of what is a weather station (*i.e*, a weather station should contain an array for wind speeds, named \"wind\", and an array for temperatures, named \"temp\").\n",
"`paris` should now be an object that answers to these specifications. Is is called an *instance* of the class `WeatherStation`.\n",
"\n",
"When defining the class, we need to define how to build it (special \"function\" `__init__`). \n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"execution_count": 8,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"max temp is 5 at index 1\n",
"wind when max_temp = 0\n"
"max temp is 5°C at index 1\n",
"wind speed at max temp = 0 km/h\n"
]
}
],
......@@ -457,19 +463,20 @@
" )\n",
"\n",
" def max_temp(self):\n",
" \"\"\" returns the maximum temperature record in the station\"\"\"\n",
" \"\"\" returns the maximum temperature recorded in the station\"\"\"\n",
" return max(self.temp)\n",
"\n",
" def arg_max_temp(self):\n",
" \"\"\" returns the index of (one of the) maximum temperature record in the station\"\"\"\n",
" \"\"\" returns the index of (one of the) maximum temperature recorded in the station\"\"\"\n",
" return self.temp.index(self.max_temp())\n",
"\n",
"\n",
"paris = WeatherStation([10, 0, 20, 30, 20, 0], [1, 5, 1, -1, -1, 3])\n",
"# OR paris = WeatherStation(wind=[10, 0, 20, 30, 20, 0], temperature=[1, 5, 1, -1, -1, 3])\n",
"idx_max_temp = paris.arg_max_temp()\n",
"\n",
"print(f\"max temp is {paris.max_temp()} at index {paris.arg_max_temp()}\")\n",
"print(f\"wind when max_temp = {paris.wind[idx_max_temp]}\")"
"print(f\"max temp is {paris.max_temp()}°C at index {paris.arg_max_temp()}\")\n",
"print(f\"wind speed at max temp = {paris.wind[idx_max_temp]} km/h\")"
]
},
{
......@@ -482,10 +489,10 @@
"source": [
"**Comments**\n",
"\n",
"The max_temp and the arg_max_temp are now part of the class. An object (here paris) thus contains both data (wind and temperature) and methods to access, process the data. \n",
"The `max_temp` and the `arg_max_temp` are now part of the class `WeatherStation`. Functions attached to classes are named **methods**.\n",
"Similary, `wind` and `temp` lists are also now part this class. Variables attached to classes are named **members** or **attributes**.\n",
"\n",
"- wind and temp are called \"members\" of the objects\n",
"- max_temp and arg_max_temp are called methods"
"An object (here `paris`) thus contains both *attributes* (holding data for example) and *methods* to access and/or process the data.\n"
]
},
{
......@@ -501,7 +508,7 @@
},
{
"cell_type": "code",
"execution_count": 8,
"execution_count": 9,
"metadata": {
"slideshow": {
"slide_type": "-"
......@@ -515,16 +522,16 @@
"test_arg_max_temp (__main__.TestWeatherStation)\n",
"test arg_max_temp function ... ok\n",
"test_building_with_bad_arrays (__main__.TestWeatherStation)\n",
"test exception is raised with bad entry ... ok\n",
"test that an exception is raised with incorrect inputs ... ok\n",
"test_building_with_good_input_arrays (__main__.TestWeatherStation)\n",
"test things goes smoothly if input are correct ... ok\n",
"test that things goes smoothly if the input are correct ... ok\n",
"test_building_with_input_iterables (__main__.TestWeatherStation)\n",
"test things goes smoothly if input are correct ... ok\n",
"test that things goes smoothly if the input are correct ... ok\n",
"test_max_temp (__main__.TestWeatherStation)\n",
"test max temp function ... ok\n",
"test test_max_temp function ... ok\n",
"\n",
"----------------------------------------------------------------------\n",
"Ran 5 tests in 0.006s\n",
"Ran 5 tests in 0.008s\n",
"\n",
"OK\n"
]
......@@ -535,30 +542,30 @@
" \"\"\"Test the weather station \"\"\"\n",
"\n",
" def setUp(self):\n",
" \"\"\"Generates a well formed station (paris)\"\"\"\n",
" \"\"\"Generates a well structured station (paris)\"\"\"\n",
" self.paris = WeatherStation(\n",
" [10, 0, 20, 30, 20, 0], \n",
" [1, 5, 1, -1, -1, 3],\n",
" )\n",
"\n",
" def test_building_with_good_input_arrays(self):\n",
" \"\"\" test things goes smoothly if input are correct\"\"\"\n",
" \"\"\" test that things goes smoothly if the input are correct\"\"\"\n",
" self.assertEqual(0, self.paris.wind[1])\n",
" self.assertEqual(5, self.paris.temp[1])\n",
"\n",
" def test_building_with_input_iterables(self):\n",
" \"\"\" test things goes smoothly if input are correct\"\"\"\n",
" \"\"\" test that things goes smoothly if the input are correct\"\"\"\n",
" r_station = WeatherStation(range(10), range(10))\n",
" self.assertEqual(4, r_station.wind[4])\n",
" self.assertEqual(5, r_station.temp[5])\n",
"\n",
" def test_building_with_bad_arrays(self):\n",
" \"\"\" test exception is raised with bad entry\"\"\"\n",
" \"\"\" test that an exception is raised with incorrect inputs\"\"\"\n",
" with self.assertRaises(ValueError):\n",
" bad_station = WeatherStation([10, 0, 20, 30, 20, 0], [1, 5, 1])\n",
"\n",
" def test_max_temp(self):\n",
" \"\"\" test max temp function\"\"\"\n",
" \"\"\" test test_max_temp function\"\"\"\n",
" self.assertEqual(5, self.paris.max_temp())\n",
"\n",
" def test_arg_max_temp(self):\n",
......@@ -580,16 +587,16 @@
"source": [
"## Do It Yourself (advanced)\n",
"\n",
"- Add a method (perceived_temp) that takes as input a temperature and wind and return the perceived temperature, i.e. taking into account the wind child effect.\n",
"- Modify max_temp and arg_max_temp so that they take an additional optional boolean parameter (*e.g.* perceived default to False). If perceived is False, the methods behave like before, if perceived is True, the temperatures considered are the perceived temperature.\n",
"- Add a method (perceived_temp) that takes as input a temperature and wind and return the perceived temperature, i.e. taking into account the wind chill effect.\n",
"- Modify max_temp and arg_max_temp so that they take an additional optional boolean parameter (*e.g.* perceived default to False). If perceived is False, the methods have the same behaviour as before. If perceived is True, the temperatures to process are the perceived temperatures.\n",
"- Code so that all the tests are ok.\n",
"- Add test for a method (perceived_temperatures) that return an array containing all the perceived temperature.\n",
"- Add a tests for a method (perceived_temperatures) that returns an array containing all the perceived temperatures.\n",
"- Code the method perceived_temperatures so that all tests are ok."
]
},
{
"cell_type": "code",
"execution_count": 9,
"execution_count": 32,
"metadata": {},
"outputs": [
{
......@@ -606,20 +613,19 @@
"test things goes smoothly if input are correct ... ok\n",
"test_max_temp_no_perceived (__main__.TestWeatherStation)\n",
"test max temp function ... ok\n",
"test_perceived (__main__.TestWeatherStation) ... FAIL\n",
"\n",
"======================================================================\n",
"FAIL: test_perceived (__main__.TestWeatherStation)\n",
"----------------------------------------------------------------------\n",
"Traceback (most recent call last):\n",
" File \"<ipython-input-9-6b5ef7e9cc6b>\", line 72, in test_perceived\n",
" self.assertEqual(-24, self.paris.perceived_temp(-20, 5))\n",
"AssertionError: -24 != -20\n",
"test_max_temp_perceived (__main__.TestWeatherStation)\n",
"test max temp function ... ok\n",
"test_perceived (__main__.TestWeatherStation)\n",
"test that the perceived temp computation works as intended ... ok\n",
"test_perceived_arrays (__main__.TestWeatherStation)\n",
"test that the perceived temp array generation works ... ok\n",
"test_perceived_with_null_wind (__main__.TestWeatherStation)\n",
"test that the perceived temp is the same without wind ... ok\n",
"\n",
"----------------------------------------------------------------------\n",
"Ran 6 tests in 0.006s\n",
"Ran 9 tests in 0.014s\n",
"\n",
"FAILED (failures=1)\n"
"OK\n"
]
}
],
......@@ -639,32 +645,50 @@
" \"wind and temperature should have the same size\"\n",
" )\n",
"\n",
" def perceived_temp(self, temp, wind):\n",
" def perceived_temp(self, air_temp, wind_speed):\n",
" \"\"\" computes the perceived temp according to \n",
" https://en.wikipedia.org/wiki/Wind_chill\n",
" i.e. apparent = 13.12 + 0.6215 air_temp - 11.37 v^0.16 + 0.3965 air_temp * v^0.16\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 temp: the temperature (in °Celcius)\n",
" :param temp: the temperature (in °Celsius)\n",
" :parent wind: the wind (in km/h)\n",
" :return: the perceived temperature\"\"\"\n",
" ### DIY correct this buggy method\n",
" return temp\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_temp_array(self):\n",
" \"\"\" Returns an array of percieved temp computed from the temperatures and wind speed data \"\"\"\n",
" apparent_temps = []\n",
" for index, temperature in enumerate(self.temp):\n",
" wind_speed = self.wind[index]\n",
" # Reusing the method perceived_temp defined above\n",
" apparent_temperature = self.perceived_temp(temperature, wind_speed)\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 == False:\n",
" return max(self.temp)\n",
" if perceived:\n",
" apparent_temp = self.perceived_temp_array()\n",
" return max(apparent_temp)\n",
" else:\n",
" ## DIY\n",
" return None\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 == False:\n",
" return self.temp.index(self.max_temp())\n",
" if perceived:\n",
" temp_array_to_search = self.perceived_temp_array()\n",
" else:\n",
" ### DIY\n",
" return None\n",
" temp_array_to_search = self.temp\n",
" return temp_array_to_search.index(self.max_temp(perceived))\n",
"\n",
"\n",
"class TestWeatherStation(unittest.TestCase):\n",
......@@ -673,13 +697,13 @@
" def setUp(self):\n",
" \"\"\"Generates a well formed station (paris)\"\"\"\n",
" self.paris = WeatherStation(\n",
" [10, 0, 20, 30, 20, 0], \n",
" [10, 50, 20, 30, 20, 0],\n",
" [1, 5, 1, -1, -1, 3],\n",
" )\n",
"\n",
" def test_building_with_good_input_arrays(self):\n",
" \"\"\" test things goes smoothly if input are correct\"\"\"\n",
" self.assertEqual(0, self.paris.wind[1])\n",
" self.assertEqual(50, self.paris.wind[1])\n",
" self.assertEqual(5, self.paris.temp[1])\n",
"\n",
" def test_building_with_input_iterables(self):\n",
......@@ -691,25 +715,56 @@
" def test_building_with_bad_arrays(self):\n",
" \"\"\" test exception is raised with bad entry\"\"\"\n",
" with self.assertRaises(ValueError):\n",
" bad_station = WeatherStation([10, 0, 20, 30, 20, 0], [1, 5, 1])\n",
" bad_station = WeatherStation([10, 50, 20, 30, 20, 0], [1, 5, 1])\n",
" \n",
" def test_perceived_with_null_wind(self):\n",
" \"\"\" test that the perceived temp is the same without wind\"\"\"\n",
" self.assertEqual(15, self.paris.perceived_temp(15, 0))\n",
"\n",
" def test_perceived(self):\n",
" self.assertEqual(15, self.paris.perceived_temp(15, 0))\n",
" \"\"\" test that the perceived temp computation works as intended\"\"\"\n",
" self.assertEqual(-24, self.paris.perceived_temp(-20, 5))\n",
" \n",
" def test_perceived_arrays(self):\n",
" \"\"\" test that the perceived temp array generation works\"\"\"\n",
" apparent_temps = self.paris.perceived_temp_array()\n",
" self.assertTrue(isinstance(apparent_temps, list))\n",
" self.assertEqual(len(apparent_temps), len(self.paris.temp))\n",
" for i in range(0, len(apparent_temps)):\n",
" self.assertLessEqual(apparent_temps[i], self.paris.temp[i])\n",
"\n",
" def test_max_temp_no_perceived(self):\n",
" \"\"\" test max temp function\"\"\"\n",
" \"\"\" test max temp function \"\"\"\n",
" self.assertEqual(5, self.paris.max_temp())\n",
"\n",
" def test_arg_max_temp_no_perceived(self):\n",
" \"\"\" test arg_max_temp function\"\"\"\n",
" self.assertEqual(1, self.paris.arg_max_temp())\n",
" \n",
" def test_max_temp_perceived(self):\n",
" \"\"\" test max temp function\"\"\"\n",
" self.assertEqual(3, self.paris.max_temp(perceived=True))\n",
"\n",
" def test_arg_max_temp_no_perceived(self):\n",
" \"\"\" test arg_max_temp function\"\"\"\n",
" self.assertEqual(5, self.paris.arg_max_temp(perceived=True))\n",
"\n",
"\n",
"suite = unittest.TestLoader().loadTestsFromTestCase(TestWeatherStation)\n",
"_res = unittest.TextTestRunner(verbosity=2).run(suite)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Comments :\n",
"- The wind array was changed to have different maximum temperatures for the air and perceived temperatures : for air temperatures, the max is 5°C (with a wind speed 50 km/h). For perceived temperatures, the max is 3°C (as the wind speed is 0).\n",
"- It was a choice to set the apparent/perceived temperature to the air temperature if the wind speed is 0 so the tests were written with this in mind. Testing such choices allows to have clear inputs/outputs.\n",
"- `isinstance` allows to test the type of an object (in this case, we test if `apparent_temps` is a list)\n",
"- When testing boolean in if structures: use `if perceived:` rather than `if perceived == True:`. It is equivalent but clearer and shorter !\n"
]
},
{
"cell_type": "markdown",
"metadata": {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment