Commit 3aee1319 authored by Mathieu Istas's avatar Mathieu Istas
Browse files

Added decorator exercise with Fibonnacci series

parent cb5796cf
......@@ -109,7 +109,7 @@
"metadata": {
"celltoolbar": "Slideshow",
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
......@@ -123,7 +123,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.6"
"version": "3.8.10"
}
},
"nbformat": 4,
......
......@@ -294,7 +294,7 @@
"metadata": {
"celltoolbar": "Slideshow",
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
......@@ -308,7 +308,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.6"
"version": "3.8.10"
}
},
"nbformat": 4,
......
......@@ -62557,7 +62557,7 @@
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
......@@ -62571,7 +62571,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.7"
"version": "3.8.10"
}
},
"nbformat": 4,
......@@ -128,10 +128,188 @@
myfunc = mydecorator(myfunc)
```
%% Cell type:markdown id: tags:
## Concrete example: Fibonacci series
%% Cell type:markdown id: tags:
Definition:
$F_0 = 0$
$F_1 = 1$
$F_n = F_{n-1} + F_{n-2}$
First elements:
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ...
%% Cell type:code id: tags:
``` python
def basic_fib(n):
# works but slow
if n < 2:
return n
else:
return basic_fib(n-1) + basic_fib(n-2)
# take some seconds
print(basic_fib(34))
```
%% Cell type:markdown id: tags:
## Fibonacci series: keep calls in memory with a dict
%% Cell type:markdown id: tags:
Basic solution can only go up to n = 30 - 35 as the number of calls grows exponentially.
<div align="middle"><img style="float: left;margin-left : 70px" src='fig/fib.png' height="500" width="500"/></div>
%% Cell type:markdown id: tags:
To overcome this limitations we will use a dictionnary that keeps the previous calls to the function in memory to avoid redundancy in function calls. This is called caching.
1: Write a function that takes an int and a dict as arguments. Use the dict to remove duplicate calls.
2: Write a general cache that takes a function as an argument and does the caching. You can then pass basic_fib as an argument and get a cached function.
3: The general cache is a decorator, use it with the @ syntax.
%% Cell type:code id: tags:
``` python
def basic_fib(n):
# reference function
if n < 2:
return n
else:
return basic_fib(n-1) + basic_fib(n-2)
# Step 1
def fib_with_dict(n, cache={}): # now takes a dict as an argument
# write the function body
return None
# bigger values should be accessible now:
print(fib_with_dict(100))
```
%% Cell type:code id: tags:
``` python
# Step 2
def general_cache(func): # take a function as an argument
# write the body of the function, do not implement the Fibonacci series here
# returns a function that behaves as the original function
# except that it checks whether the result is already stored in a dict
return cached_func
```
%% Cell type:code id: tags:
``` python
# Step 3, profit! Nothing to do, your general_cache should work as a decorator
# uncomment when general_cache is ready
# @general_cache
def basic_fib_decorated(n):
if n < 2:
return n
else:
return basic_fib_decorated(n-1) + basic_fib_decorated(n-2)
```
%% Cell type:markdown id: tags:
## Solution step 1: use a dict as an argument
%% Cell type:code id: tags:
``` python
# one possible solution
def fib_with_dict(n, cache={}):
if n in cache:
return cache[n]
else:
if n < 2:
return n
else:
cache[n] = fib_with_dict(n-1, cache) + fib_with_dict(n-2, cache)
return cache[n]
print(fib_with_dict(100))
```
%% Cell type:markdown id: tags:
## Solution step 2: a more general cache
%% Cell type:code id: tags:
``` python
def basic_fib(n):
# do not touch
if n < 2:
return n
else:
return basic_fib(n-1) + basic_fib(n-2)
def general_cache(func):
# Take a function as an argument
cache = {}
def cached_func(*args):
if args not in cache:
cache[args] = func(*args)
return cache[args]
# returns a function that behaves as the original function
# except that it checks whether the result is already stored in a dict
return cached_func
basic_fib = general_cache(basic_fib)
print(basic_fib(100))
```
%% Cell type:markdown id: tags:
## With a standard library
%% Cell type:code id: tags:
``` python
from functools import lru_cache
@lru_cache(maxsize=1024)
def basic_fib_decorated(n):
if n < 2:
return n
else:
return fib(n-1) + fib(n-2)
```
%% Cell type:markdown id: tags:
Bonus question:
Why is the first line cached but not the second one? (Try yourself)
%% Cell type:code id: tags:
``` python
basic_fib = general_cache(basic_fib)
basic_fib_cached = general_cache(basic_fib)
```
%% Cell type:markdown id: tags:
## Numba: (per-method) JIT for Python-Numpy code
%% Cell type:raw id: tags:
<div align="middle">
......@@ -452,5 +630,11 @@
- Many good accelerators and compilers for Python-Numpy code
- Very good results for "standard types" (arrays of numbers on CPU and GPU)
- Limitation: high performance with user-defined numerical types
%% Cell type:code id: tags:
``` python
```
......
......@@ -211,9 +211,9 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.2"
"version": "3.8.10"
}
},
"nbformat": 4,
"nbformat_minor": 2
"nbformat_minor": 4
}
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