Commit 81c9fcd8 authored by Franck Thollard's avatar Franck Thollard
Browse files

using unittest for the DIY

parent 29b47f03
......@@ -88,13 +88,53 @@
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"test_add (__main__.TestMyStuff0)\n",
"test max_temp computes correctly ... "
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"result = 5\n",
"result = aabb\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"ok\n",
"\n",
"----------------------------------------------------------------------\n",
"Ran 1 test in 0.004s\n",
"\n",
"OK\n"
]
}
],
"source": [
"import unittest\n",
"\n",
"def add(arg0, arg1):\n",
" \"\"\"Print and return the sum of the two arguments (duck typing).\"\"\"\n",
" result = arg0 + arg1\n",
" print('result = ', result)\n",
" return result"
" return result\n",
"\n",
"\n",
"class TestMyStuff(unittest.TestCase): \n",
" def test_add(self):\n",
" \"\"\" test max_temp computes correctly\"\"\"\n",
" self.assertEqual(8, add(3, 5))\n",
" self.assertEqual(\"aabb\", add(\"aa\", \"bb\"))\n",
"\n",
"_res = unittest.TextTestRunner(verbosity=2).run(unittest.TestLoader().loadTestsFromTestCase(TestMyStuff0))"
]
},
{
......@@ -164,6 +204,65 @@
"Write a function that returns the sum of the first argument with twice the second argument."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"test_add_second_twice (__main__.TestMyStuff0)\n",
"test max_temp computes correctly ... "
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"arg0 + 2*arg1 = 3 + 2*5 = 13\n",
"arg0 + 2*arg1 = aa + 2*bb = aabbbb\n",
"arg0 + 2*arg1 = [1, 2] + 2*[3, 4] = [1, 2, 3, 4, 3, 4]\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"ok\n",
"\n",
"----------------------------------------------------------------------\n",
"Ran 1 test in 0.003s\n",
"\n",
"OK\n"
]
}
],
"source": [
"import unittest\n",
"\n",
"def add_second_twice(arg0, arg1):\n",
" \"\"\"Return the sum of the first argument with twice the second one.\n",
" Arguments should be of type that support sum and product by \n",
" an integer (e.g. numerical, string, list, ...)\n",
" :param arg0: first argument\n",
" :param arg1: second argument \n",
" :return: arg0 + 2 * arg1\n",
" \"\"\"\n",
" pass\n",
"\n",
"\n",
"class TestMyStuff0(unittest.TestCase): \n",
" def test_add_second_twice(self):\n",
" \"\"\" test max_temp computes correctly\"\"\"\n",
" self.assertEqual(13, add_second_twice(3, 5))\n",
" self.assertEqual(\"aabbbb\", add_second_twice(\"aa\", \"bb\"))\n",
" self.assertListEqual([1, 2, 3, 4, 3, 4], add_second_twice([1, 2], [3, 4])) \n",
" \n",
"_res = unittest.TextTestRunner(verbosity=2).run(unittest.TestLoader().loadTestsFromTestCase(TestMyStuff0))"
]
},
{
"cell_type": "markdown",
"metadata": {
......@@ -205,8 +304,14 @@
}
],
"source": [
"def myfunc(arg0, arg1):\n",
" \"Return the sum of the first argument with twice the second one.\"\n",
"def add_second_twice(arg0, arg1):\n",
" \"\"\"Return the sum of the first argument with twice the second one.\n",
" Arguments should be of type that support sum and product by \n",
" an integer (e.g. numerical, string, list, ...)\n",
" :param arg0: first argument\n",
" :param arg1: second argument \n",
" :return: arg0 + 2 * arg1\n",
" \"\"\"\n",
" result = arg0 + 2*arg1\n",
" print(f'arg0 + 2*arg1 = {arg0} + 2*{arg1} = {result}')\n",
" return result\n",
......@@ -375,27 +480,29 @@
},
{
"cell_type": "code",
"execution_count": 11,
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[4] 2\n"
"das f [17, 4]\n",
"[17] 2\n"
]
}
],
"source": [
"number = 2\n",
"mylist = []\n",
"mylist = [17]\n",
"\n",
"def my_strange_append_square(mylist, number):\n",
" # new function namespace with names \"mylist\" and \"number\"\n",
" number = number**2\n",
" mylist.append(number)\n",
" mylist.append( number)\n",
" print(\"das f\", mylist)\n",
" \n",
"my_strange_append_square(mylist, number)\n",
"my_strange_append_square(mylist[:], number)\n",
"print(mylist, number)"
]
},
......@@ -551,7 +658,7 @@
},
{
"cell_type": "code",
"execution_count": 15,
"execution_count": 7,
"metadata": {},
"outputs": [
{
......@@ -567,7 +674,7 @@
"16"
]
},
"execution_count": 15,
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
......@@ -648,23 +755,28 @@
},
{
"cell_type": "code",
"execution_count": 17,
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"arg0 + 2*arg1 = 1 + 2*0 = 1\n"
"arg0 + 2*arg1 = a + 2*n = ann\n"
]
}
],
"source": [
"def myfunc1(arg0, arg1=0):\n",
"def myfunc1(arg0, arg1=None):\n",
" \"Return the sum of the first argument with twice the second one.\"\n",
" if arg1 is None:\n",
" if type(arg0) == int:\n",
" arg1 = 0\n",
" else:\n",
" arg1 = \"\"\n",
" myfunc(arg0, arg1)\n",
"\n",
"myfunc1(1)"
"myfunc1(\"a\", \"n\")"
]
},
{
......@@ -699,18 +811,18 @@
},
{
"cell_type": "code",
"execution_count": 19,
"execution_count": 15,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"([],)"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
"name": "stdout",
"output_type": "stream",
"text": [
"[1]\n",
"[1, 1]\n",
"[1, 1, 1]\n",
"[1, 1, 1, 1]\n"
]
}
],
"source": [
......@@ -718,7 +830,10 @@
" l.append(1)\n",
" print(l)\n",
"\n",
"do_not_use_mutable_object_for_default_arg.__defaults__"
"do_not_use_mutable_object_for_default_arg()\n",
"do_not_use_mutable_object_for_default_arg()\n",
"do_not_use_mutable_object_for_default_arg()\n",
"do_not_use_mutable_object_for_default_arg()"
]
},
{
......@@ -777,7 +892,7 @@
},
{
"cell_type": "code",
"execution_count": 22,
"execution_count": 16,
"metadata": {},
"outputs": [
{
......@@ -786,8 +901,19 @@
"text": [
"[1]\n",
"[1]\n",
"[1]\n"
"[1]\n",
"[1, 2, 3, 1]\n"
]
},
{
"data": {
"text/plain": [
"[1, 2, 3, 1]"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
......@@ -799,7 +925,10 @@
"\n",
"how_to_use_list_as_default_arg()\n",
"how_to_use_list_as_default_arg()\n",
"how_to_use_list_as_default_arg()"
"how_to_use_list_as_default_arg()\n",
"l1 = [1,2,3]\n",
"how_to_use_list_as_default_arg(l1)\n",
"l1"
]
},
{
......@@ -875,7 +1004,7 @@
},
{
"cell_type": "code",
"execution_count": 24,
"execution_count": 19,
"metadata": {},
"outputs": [
{
......@@ -884,17 +1013,17 @@
"text": [
"call:\n",
"\ta = 1\n",
"\targs = (2, 3)\n",
"\targs = (3,)\n",
"\tkwargs = {'toto': 3, 'titi': 3}\n",
"call:\n",
"\ta = a\n",
"\targs = ('b',)\n",
"\targs = ()\n",
"\tkwargs = {'bob': 3}\n"
]
}
],
"source": [
"def func(a, *args, **kwargs):\n",
"def func(a, b, *args, **kwargs):\n",
" print(f'call:\\n\\ta = {a}\\n\\targs = {args}\\n\\tkwargs = {kwargs}')\n",
" \n",
"func(1, 2, 3, toto=3, titi=3)\n",
......@@ -915,6 +1044,103 @@
"all the elements of the list by the number. If not set, number is defaulted to 2."
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"test_mult_list_int_defaut (__main__.TestMyStuff0) ... FAIL\n",
"test_mult_list_int_with_factor (__main__.TestMyStuff0) ... FAIL\n",
"test_mult_list_str_with_factor (__main__.TestMyStuff0) ... FAIL\n",
"\n",
"======================================================================\n",
"FAIL: test_mult_list_int_defaut (__main__.TestMyStuff0)\n",
"----------------------------------------------------------------------\n",
"Traceback (most recent call last):\n",
" File \"<ipython-input-23-4858bef3312c>\", line 14, in test_mult_list_int_defaut\n",
" self.assertListEqual(l_target, l)\n",
"AssertionError: Lists differ: [2, 6, 10] != [1, 3, 5]\n",
"\n",
"First differing element 0:\n",
"2\n",
"1\n",
"\n",
"- [2, 6, 10]\n",
"+ [1, 3, 5]\n",
"\n",
"======================================================================\n",
"FAIL: test_mult_list_int_with_factor (__main__.TestMyStuff0)\n",
"----------------------------------------------------------------------\n",
"Traceback (most recent call last):\n",
" File \"<ipython-input-23-4858bef3312c>\", line 8, in test_mult_list_int_with_factor\n",
" self.assertListEqual(l_target, l)\n",
"AssertionError: Lists differ: [2, 6, 10] != [1, 3, 5]\n",
"\n",
"First differing element 0:\n",
"2\n",
"1\n",
"\n",
"- [2, 6, 10]\n",
"+ [1, 3, 5]\n",
"\n",
"======================================================================\n",
"FAIL: test_mult_list_str_with_factor (__main__.TestMyStuff0)\n",
"----------------------------------------------------------------------\n",
"Traceback (most recent call last):\n",
" File \"<ipython-input-23-4858bef3312c>\", line 20, in test_mult_list_str_with_factor\n",
" self.assertListEqual(l_target, l)\n",
"AssertionError: Lists differ: ['aa', 'bb', 'cc'] != ['a', 'b', 'c']\n",
"\n",
"First differing element 0:\n",
"'aa'\n",
"'a'\n",
"\n",
"- ['aa', 'bb', 'cc']\n",
"? - - -\n",
"\n",
"+ ['a', 'b', 'c']\n",
"\n",
"----------------------------------------------------------------------\n",
"Ran 3 tests in 0.007s\n",
"\n",
"FAILED (failures=3)\n"
]
}
],
"source": [
"import unittest\n",
"\n",
"class TestMyStuff0(unittest.TestCase): \n",
" def test_mult_list_int_with_factor(self):\n",
" l = [1, 3, 5]\n",
" l_target = [2, 6, 10]\n",
" mult_list(l, 2)\n",
" self.assertListEqual(l_target, l)\n",
" \n",
" def test_mult_list_int_defaut(self):\n",
" l = [1, 3, 5]\n",
" l_target = [2, 6, 10]\n",
" mult_list(l, 2)\n",
" self.assertListEqual(l_target, l)\n",
" \n",
" def test_mult_list_str_with_factor(self):\n",
" l = [\"a\", \"b\", \"c\"]\n",
" l_target = [\"aa\", \"bb\", \"cc\"]\n",
" mult_list(l, 2)\n",
" self.assertListEqual(l_target, l)\n",
"\n",
"def mult_list(my_list, factor):\n",
" \"Multiply each element of my_list by factor (default=2)\"\n",
" pass\n",
"\n",
"\n",
"_res = unittest.TextTestRunner(verbosity=2).run(unittest.TestLoader().loadTestsFromTestCase(TestMyStuff0))"
]
},
{
"cell_type": "markdown",
"metadata": {
......@@ -928,15 +1154,34 @@
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {
"collapsed": true
},
"outputs": [],
"execution_count": 22,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[0, 1, 2]\n",
"[0, 2, 4]\n",
"[0, 8, 16]\n",
"['aaaa', 'bbbb']\n"
]
}
],
"source": [
"def func(l, a=2):\n",
" for i, val in enumerate(l):\n",
" l[i] = a * val"
" l[i] = a * val\n",
" \n",
"l = list(range(3))\n",
"print(l)\n",
"func(l)\n",
"print(l)\n",
"func(l, 4)\n",
"print(l)\n",
"l = ['a', 'b']\n",
"func(l, 4)\n",
"print(l)\n"
]
},
{
......@@ -1094,7 +1339,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.6"
"version": "3.7.3rc1"
}
},
"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)
# 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: stream
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: stream
test_add (__main__.TestMyStuff0)
test max_temp computes correctly ...
%%%% Output: stream
result = 5
result = aabb
%%%% Output: stream
ok
----------------------------------------------------------------------
Ran 1 test in 0.004s
OK
%% Cell type:code id: tags:
``` python
add(2, 3)
```
%%%% Output: stream
result = 5
%%%% Output: execute_result
5
%% Cell type:code id: tags:
``` python
add('a', 'b')
```
%%%% Output: stream
result = ab
%%%% Output: execute_result
'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: stream
test_add_second_twice (__main__.TestMyStuff0)
test max_temp computes correctly ...
%%%% Output: stream
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]
%%%% Output: stream
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 myfunc(arg0, arg1):
"Return the sum of the first argument with twice the second one."
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: stream
arg0 + 2*arg1 = 4 + 2*6 = 16
%%%% Output: execute_result
16
%% Cell type:code id: tags:
``` python
myfunc('a', 'b')
```
%%%% Output: stream
arg0 + 2*arg1 = a + 2*b = abb
%%%% Output: execute_result
'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: stream
%%%% Output: execute_result
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: stream
(1, 'hello', 3)
%% Cell type:code id: tags:
``` python
a, b, c = return_a_tuple()
print(b)
```
%%%% Output: stream
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: stream
[4] 2
%% Cell type:code id: tags:
``` python
number = 2
mylist = []
mylist = [17]
def my_strange_append_square(mylist, number):
# new function namespace with names "mylist" and "number"
number = number**2
mylist.append(number)
mylist.append( number)
print("das f", mylist)
my_strange_append_square(mylist, number)
my_strange_append_square(mylist[:], number)
print(mylist, number)
```
%%%% Output: stream
[4] 2
das f [17, 4]
[17] 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