Je viens de découvrir ces deux projets très chousettes : tutormagic et nbtutor, tous les deux lias bres, gratuits, en Python, et proposant d'intégrer des visualisations intéractives, venant ou inspirées du merveilleux PythonTutor.com.
Peut-on utiliser l'un des deux en mode offline (sans une iframe vers PythonTutor) ? Je pense que non pour tutormagic, je pense que oui pour nbtutor.
Est-ce que ça marche bien pour tous les types de Python de base : booléens/entiers/flottants, chaînes, objets, nombres complexes, tableau/list
, dictionnaire/ensemble ? Probablement ! Réponse : oui !
Et avec des tableaux numpy ? Probablement !. Réponse : oui pour tutormagic
avec ma nouvelle version qui ajoute le mode "py3anaconda" (à voir si cette PR est acceptée !)
tutormagic
peut-être, tant qu'on ne génère pas de figure... avec nbtutor
apparemment oui.tutormagic
en mode "py3anaconda" supporte plein de modules scientifiques puisque Anaconda 5.2 supporte plein de trucs : scipy, sympa, scikit-learn, et j'en passe !Jupyter a beaucoup changé ces dernières anneés, entre 2018 et 2021..
Mais ave jupyter classique, normalement ça va les "vieilles" extensions fonctionnent encore. Jupyter Lab, c'est une autre histoire !
pip
, et s'installe en une commande !Dans mon installation de Python 3, j'ai installé les deux paquets tutormagic
et nbtutor
Où installer ?
sudo pip install
;pip install
(ou avec conda install
mais pas testé).%%bash
echo "Démonstration (pas exécutée)"
exit 0 # enlevez ça pour refaire ça chez vous
# tapez ça dans votre terminal
pip install tutormagic nbtutor
# si ça marche pas, rajoutez sudo
sudo pip install tutormagic nbtutor
# il faut manuellement installer et activer nbtutor
jupyter nbextension install --overwrite --py nbtutor
jupyter nbextension enable --py nbtutor
Démonstration (pas exécutée)
Et donc ce notebook peut déclarer ses dépendances :
%load_ext watermark
watermark -a "Lilian Besson (Naereen)" -p tutormagic,nbtutor,numpy,matplotlib
Lilian Besson (Naereen) tutormagic 0.3.0 nbtutor 1.0.4 numpy 1.19.2 matplotlib 3.3.2
test = "Démo de jupyter notebook"
print(test)
somme = 0
MAX_I = 10
for i in range(1, MAX_I):
somme += i**3
print(f"Somme des {MAX_I} premiers cubes = {somme}")
Démo de jupyter notebook Somme des 10 premiers cubes = 2025
Je vais emprunter le petit problème arithmético-algorithmique suivant, posé à mes élèves au début de février 2021 :
Mini challenge algorithmique pour les passionnés en manque de petits exercices de code : (optionnel) Vous avez dû observer que ce mois de février est spécial parce que le 1er février est un lundi, et qu'il a exactement 4 lundis, 4 mardis, 4 mercredis, 4 jeudis, 4 vendredis, 4 samedis et 4 dimanches.
Question : Comptez le nombre de mois de février répondant à ce critère (je n'ai pas trouvé de nom précis), depuis l'année de création de l'ENS Rennes (1994, enfin pour Cachan antenne Bretagne) jusqu'à 2077 (1994 et 2077 inclus).
Pour plus d'informations, cf ce notebook (aussi sur GitHub et nbviewer).
Avec le module calendar on pourrait faire comme en Bash : imprimer les calendriers, et rechercher des chaînes particulières... mais ce n'est pas très propre. Essayons avec ce même module mais en écrivant une solution fonctionnelle !
import calendar
def filter_annee(annee):
return (
set(calendar.Calendar(annee).itermonthdays2(annee, 2))
& {(1,0), (28, 6), (29, 0)}
) == {(1, 0), (28, 6)}
filter_annee(2020), filter_annee(2021), filter_annee(2022)
(False, True, False)
C'est un bon exemple car il utilise des listes, des ensembles etc. Peut-être restreindre le nombre d'année considérées, pour simplifier ?
Et donc on a juste à compter les années, de 1994 à 2077 inclus, qui ne sont pas des années bissextiles et qui satisfont le filtre :
%%time
len(list(filter(filter_annee, ( annee
for annee in range(1994, 2077 + 1)
# if not calendar.isleap(annee) # en fait c'est inutile
)
)))
CPU times: user 1.16 ms, sys: 696 µs, total: 1.85 ms Wall time: 1.87 ms
9
tutormagic
¶Je suis le tutoriel présent sur la documentation.
%load_ext tutormagic
The tutormagic extension is already loaded. To reload it, use: %reload_ext tutormagic
%%tutor --lang python3 --height 500
URL = "http://PythonTutor.com"
test = "Démo de tutormagic"
explication = f"avec un iframe vers {URL}"
print(test, explication)
somme = 0
MAX_I = 10
for i in range(1, MAX_I):
somme += i**3
print(f"Somme des {MAX_I} premiers cubes = {somme}")
Ca marche bien pour ce petit exemple !
Essayons plus solide :
%%tutor --lang python3 --run --height 500 --curInstr 11
import calendar
def filter_annee(annee):
return (
set(calendar.Calendar(annee).itermonthdays2(annee, 2))
& {(1,0), (28, 6), (29, 0)}
) == {(1, 0), (28, 6)}
nb_bonnes_annees = len(list(filter(filter_annee, ( annee
for annee in range(1994, 2077 + 1)
# if not calendar.isleap(annee) # en fait c'est inutile
)
)))
Whooo! Ca a pris 763 étapes, on était pas loin de la limite des 1000 étapes sur PythonTutor.com, mais ça marche !
Ca marche vraiment bien !
En repliant le code (avec une extension, Codefolding), on peut montrer juste le début (la "magic" %%tutor
) et l'iframe !
Bonus : ça fonctionne si on ferme le notebook et qu'on le rouvre, sans même avoir à réexécuter la cellule ! Très pratique pour préparer son cours !
nbtutor
¶Je suis aussi la documentation sur la page GitHub de nbtutor.
%reload_ext nbtutor
Ca semble bien chargé, essayons !
%%nbtutor --reset --force --debug
explication = "Démo de nbtutor, normalement sans iframe vers PythonTutor.com"
print(explication)
somme = 0
MAX_I = 10
for i in range(1, MAX_I):
somme += i**3
print(f"Somme des {MAX_I} premiers cubes = {somme}")
Ca marche pas :( Apparemment ça marche pas bien si tutormagic a aussi été exécuté...
Je vois sur la documentation du projet que l'installation de l'extension ajoute une "barre d'outil de cellule", mais je ne l'ai pas... je l'ai désormais, en rechargeant le notebook !
Elle prend la place de la barre d'outil pour les slides RISE, mais ce n'est pas grave, on peut passer de l'un à l'autre.
Je rencontre le même problème que ce ticket.
Essayons de comprendre ce problème de nbtutor !!
!pip show nbtutor
Name: nbtutor Version: 1.0.4 Summary: Visualize Python code execution in Jupyter Notebook cells Home-page: https://github.com/lgpage/nbtutor Author: Logan Page Author-email: page.lg@gmail.com License: BSD 3-Clause Location: /usr/local/lib/python3.6/dist-packages Requires: notebook Required-by:
!jupyter-nbextension list 2>&1 | grep -A 2 nbtutor
nbtutor/js/nbtutor.min enabled - Validating: OK tree section
Bon, le package semble installé, et ajouté comme une extension Jupyter, qui le reconnaît et la valide...
Chargez l'extension avec %load_ext nbtutor
a fonctionné... juste la magic %%nbtutor
ne marche pas.
%%nbtutor?
Sa documentation se charge bien !
Docstring:
::
%nbtutor [-r] [-f] [-i] [-d N] [--digits D] [--max_size S] [--step_all]
[--expand_arrays] [--nolies] [--debug]
Mais rien ne se passe ! Même en mode --debug
...
%%nbtutor --reset --force --debug
x = sum([i**3 for i in range(1, 10)])
print(x)
Aucun message d'erreur, rien dans la console qui a lancé Jupyter...
Je pense que l'extension est cassée avec les nouvelles variantes de Python ou de Jupyter, qui comme je le suspectais a beaucoup changé depuis 2016 (dernier commit de ce projet)...
J'ai demandé au développeur s'il maintenait encore son projet.
Et pour l'exemple plus gros :
%%nbtutor --reset --force
import calendar
def filter_annee(annee):
return (
set(calendar.Calendar(annee).itermonthdays2(annee, 2))
& {(1,0), (28, 6), (29, 0)}
) == {(1, 0), (28, 6)}
nb_bonnes_annees = len(list(filter(filter_annee, ( annee
for annee in range(1994, 2077 + 1)
# if not calendar.isleap(annee) # en fait c'est inutile
)
)))
Ca marche ! Wooo!
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
X = np.linspace(-10, 10)
def f1(x): return np.cos(x**2)
def f2(x): return np.sin(x**2)
def f3(x): return f1(x)+f2(x)
Y1 = f1(X)
Y2 = f2(X)
Y3 = f3(X)
# avec la police "Humour Sans", téléchargée depuis https://aur.archlinux.org/packages/ttf-humor-sans/
with plt.xkcd():
plt.figure(figsize=(14,8))
plt.plot(X, Y1, "+-r", label="$f_1(x)$", ms=10, lw=3)
plt.plot(X, Y2, "o-b", label="$f_2(x)$", ms=10, lw=3)
plt.plot(X, Y3, "d-y", label="$f_3(x)$", ms=10, lw=3)
plt.legend()
plt.xlabel("Axe $x$")
plt.ylabel("Axe $y$")
plt.title("Trois fonctions de la variable réelle")
plt.show()
tutormagic
?¶%%tutor --lang python3 --height 100
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
X = np.linspace(-10, 10)
def f1(x): return np.cos(x**2)
def f2(x): return np.sin(x**2)
def f3(x): return f1(x)+f2(x)
Y1 = f1(X)
Y2 = f2(X)
Y3 = f3(X)
with plt.xkcd():
plt.figure(figsize=(14,8))
plt.plot(X, Y1, "+-r", label="$f_1(x)$", ms=10, lw=3)
plt.plot(X, Y2, "o-b", label="$f_2(x)$", ms=10, lw=3)
plt.plot(X, Y3, "d-y", label="$f_3(x)$", ms=10, lw=3)
plt.legend()
plt.xlabel("Axe $x$")
plt.ylabel("Axe $y$")
plt.title("Trois fonctions de la variable réelle")
plt.show()
Ici, le problème vient de la "cell magic" %matplotlib
en ligne 4 !
Et si on essaie sans cette cell magic ?
%%tutor --lang python3 --height 500
import numpy as np
import matplotlib.pyplot as plt
X = np.linspace(-10, 10)
def f1(x): return np.cos(x**2)
def f2(x): return np.sin(x**2)
def f3(x): return f1(x)+f2(x)
Y1 = f1(X)
Y2 = f2(X)
Y3 = f3(X)
print(Y1)
print(Y2)
print(Y3)
Evidemment, ça ne marche pas ! Numpy et matplotlib ne sont PAS supportés dans PythonTutor.com...
Seul les modules suivants sont disponibles avec le mode "Python3" basique :
Only these modules can be imported:
__future__, abc, array, bisect, calendar, cmath,
collections, copy, datetime, decimal, doctest, fractions,
functools, hashlib, heapq, io, itertools, json,
locale, math, operator, pickle, pprint, random,
re, string, time, types, typing, unittest
Ca allait être un avantage de nbtutor... au moins la partie numpy.
Bon, et bien on peut essayer "Python 3.6 with Anaconda (experimental)".
Depuis le site web, j'arrive à utiliser Python 3.6 + Anaconda 5.2 EXPERIMENTAL! pour le code suivant.
%%tutor --lang py3anaconda --height 500
import numpy as np
X = np.linspace(-10, 10)
def f1(x): return np.cos(x**2)
def f2(x): return np.sin(x**2)
def f3(x): return f1(x)+f2(x)
Y1 = f1(X)
Y2 = f2(X)
Y3 = f3(X)
print(Y1)
print(Y2)
print(Y3)
--------------------------------------------------------------------------- ValueError Traceback (most recent call last) <ipython-input-18-f2046576b7cf> in <module> ----> 1 get_ipython().run_cell_magic('tutor', '--lang py33anaconda --height 500', '\nimport numpy as np\n\nX = np.linspace(-10, 10)\n\ndef f1(x): return np.cos(x**2)\ndef f2(x): return np.sin(x**2)\ndef f3(x): return f1(x)+f2(x) \nY1 = f1(X)\nY2 = f2(X)\nY3 = f3(X)\n\nprint(Y1)\nprint(Y2)\nprint(Y3)\n') /usr/local/lib/python3.6/dist-packages/IPython/core/interactiveshell.py in run_cell_magic(self, magic_name, line, cell) 2369 with self.builtin_trap: 2370 args = (magic_arg_s, cell) -> 2371 result = fn(*args, **kwargs) 2372 return result 2373 <decorator-gen-126> in tutor(self, line, cell, local_ns) /usr/local/lib/python3.6/dist-packages/IPython/core/magic.py in <lambda>(f, *a, **k) 185 # but it's overkill for just that one bit of state. 186 def magic_deco(arg): --> 187 call = lambda f, *a, **k: f(*a, **k) 188 189 if callable(arg): /usr/local/lib/python3.6/dist-packages/tutormagic.py in tutor(self, line, cell, local_ns) 150 "{} not supported. Only the following options are allowed: " 151 "'python2', 'python3', 'java', 'javascript', " --> 152 "'typescript', 'ruby', 'c', 'c++', 'py3anaconda'".format(args.lang[0])) 153 else: 154 lang = "python3" ValueError: py33anaconda not supported. Only the following options are allowed: 'python2', 'python3', 'java', 'javascript', 'typescript', 'ruby', 'c', 'c++', 'py3anaconda'
%%tutor --lang py3anaconda --height 500
# https://docs.scipy.org/doc/scipy/reference/tutorial/integrate.html
x = 4
print("Hello world!" * x)
import numpy as np
I = np.sqrt(2/np.pi)*(18.0/27*np.sqrt(2)*np.cos(4.5) - 4.0/27*np.sqrt(2)*np.sin(4.5))
print(I)
import scipy.integrate as integrate
import scipy.special as special
result = integrate.quad(lambda x: special.jv(2.5,x), 0, 4.5)
print(result)
I += np.sqrt(2*np.pi) * special.fresnel(3/np.sqrt(np.pi))[0]
print(I)
print(abs(result[0]-I))
POUF ça marche, merci à ma petite modification (trois lignes changées).
C'est quand même beau les logiciels open-source...
Au fait, PythonTutor n'est plus libre, mais un fork récent existe : c'est peut-être un vol de propriété intellectuelle, mais bon, ça peut dépanner !
TODO: l'installer chez moi, et faire en sorte que tutormagic fonctionne en local aussi ! Regardez ce ticket assez récent, mais pas avancé (mais très détaillé), et celui là sur un clone de PythonTutor.
=> J'ai réussi à avoir PyTutor (v5-unity) mais juste pour Python3... pas C ou Java... et je sais pas comment faire !
nbtutor
?¶%%nbtutor --reset --force --debug
import numpy as np
import matplotlib.pyplot as plt
X = np.linspace(-10, 10, 40)
def f1(x): return np.cos(x**2)
def f2(x): return np.sin(x**2)
def f3(x): return f1(x)+f2(x)
Y1 = f1(X)
Y2 = f2(X)
Y3 = f3(X)
with plt.xkcd():
plt.figure(figsize=(14,8))
plt.plot(X, Y1, "+-r", label="$f_1(x)$", ms=10, lw=3)
#plt.plot(X, Y2, "o-b", label="$f_2(x)$", ms=10, lw=3)
#plt.plot(X, Y3, "d-y", label="$f_3(x)$", ms=10, lw=3)
plt.legend()
#plt.xlabel("Axe $x$")
#plt.ylabel("Axe $y$")
plt.title("Trois fonctions de la variable réelle")
plt.show()
--------------------------------------------------------------------------- KeyboardInterrupt Traceback (most recent call last) /usr/local/lib/python3.6/dist-packages/nbtutor/ipython/debugger.py in run_cell(self, cell) 47 with redirect_stdout(self.stdout): ---> 48 self.run(cell, globals, locals) 49 except: /usr/lib/python3.6/bdb.py in run(self, cmd, globals, locals) 433 try: --> 434 exec(cmd, globals, locals) 435 except BdbQuit: <string> in <module> /usr/local/lib/python3.6/dist-packages/matplotlib/pyplot.py in show(*args, **kwargs) 352 _warn_if_gui_out_of_main_thread() --> 353 return _backend_mod.show(*args, **kwargs) 354 /usr/local/lib/python3.6/dist-packages/ipykernel/pylab/backend_inline.py in show(close, block) 42 figure_manager.canvas.figure, ---> 43 metadata=_fetch_figure_metadata(figure_manager.canvas.figure) 44 ) /usr/local/lib/python3.6/dist-packages/IPython/core/display.py in display(include, exclude, metadata, transient, display_id, *objs, **kwargs) 312 else: --> 313 format_dict, md_dict = format(obj, include=include, exclude=exclude) 314 if not format_dict: /usr/local/lib/python3.6/dist-packages/IPython/core/formatters.py in format(self, obj, include, exclude) 179 try: --> 180 data = formatter(obj) 181 except: <decorator-gen-9> in __call__(self, obj) /usr/local/lib/python3.6/dist-packages/IPython/core/formatters.py in catch_format_error(method, self, *args, **kwargs) 223 try: --> 224 r = method(self, *args, **kwargs) 225 except NotImplementedError: /usr/local/lib/python3.6/dist-packages/IPython/core/formatters.py in __call__(self, obj) 340 else: --> 341 return printer(obj) 342 # Finally look for special method names /usr/local/lib/python3.6/dist-packages/IPython/core/pylabtools.py in <lambda>(fig) 247 if 'png' in formats: --> 248 png_formatter.for_type(Figure, lambda fig: print_figure(fig, 'png', **kwargs)) 249 if 'retina' in formats or 'png2x' in formats: /usr/local/lib/python3.6/dist-packages/IPython/core/pylabtools.py in print_figure(fig, fmt, bbox_inches, **kwargs) 131 --> 132 fig.canvas.print_figure(bytes_io, **kw) 133 data = bytes_io.getvalue() /usr/local/lib/python3.6/dist-packages/matplotlib/backend_bases.py in print_figure(self, filename, dpi, facecolor, edgecolor, orientation, format, bbox_inches, pad_inches, bbox_extra_artists, backend, **kwargs) 2192 with ctx: -> 2193 self.figure.draw(renderer) 2194 /usr/local/lib/python3.6/dist-packages/matplotlib/artist.py in draw_wrapper(artist, renderer, *args, **kwargs) 40 ---> 41 return draw(artist, renderer, *args, **kwargs) 42 finally: /usr/local/lib/python3.6/dist-packages/matplotlib/figure.py in draw(self, renderer) 1863 mimage._draw_list_compositing_images( -> 1864 renderer, self, artists, self.suppressComposite) 1865 /usr/local/lib/python3.6/dist-packages/matplotlib/image.py in _draw_list_compositing_images(renderer, parent, artists, suppress_composite) 130 for a in artists: --> 131 a.draw(renderer) 132 else: /usr/local/lib/python3.6/dist-packages/matplotlib/artist.py in draw_wrapper(artist, renderer, *args, **kwargs) 40 ---> 41 return draw(artist, renderer, *args, **kwargs) 42 finally: /usr/local/lib/python3.6/dist-packages/matplotlib/cbook/deprecation.py in wrapper(*inner_args, **inner_kwargs) 410 **kwargs) --> 411 return func(*inner_args, **inner_kwargs) 412 /usr/local/lib/python3.6/dist-packages/matplotlib/axes/_base.py in draw(self, renderer, inframe) 2746 -> 2747 mimage._draw_list_compositing_images(renderer, self, artists) 2748 /usr/local/lib/python3.6/dist-packages/matplotlib/image.py in _draw_list_compositing_images(renderer, parent, artists, suppress_composite) 130 for a in artists: --> 131 a.draw(renderer) 132 else: /usr/local/lib/python3.6/dist-packages/matplotlib/artist.py in draw_wrapper(artist, renderer, *args, **kwargs) 40 ---> 41 return draw(artist, renderer, *args, **kwargs) 42 finally: /usr/local/lib/python3.6/dist-packages/matplotlib/axis.py in draw(self, renderer, *args, **kwargs) 1163 -> 1164 ticks_to_draw = self._update_ticks() 1165 ticklabelBoxes, ticklabelBoxes2 = self._get_tick_bboxes(ticks_to_draw, /usr/local/lib/python3.6/dist-packages/matplotlib/axis.py in _update_ticks(self) 1022 major_labels = self.major.formatter.format_ticks(major_locs) -> 1023 major_ticks = self.get_major_ticks(len(major_locs)) 1024 self.major.formatter.set_locs(major_locs) /usr/local/lib/python3.6/dist-packages/matplotlib/axis.py in get_major_ticks(self, numticks) 1381 # Update the new tick label properties from the old. -> 1382 tick = self._get_tick(major=True) 1383 self.majorTicks.append(tick) /usr/local/lib/python3.6/dist-packages/matplotlib/axis.py in _get_tick(self, major) 2012 tick_kw = self._minor_tick_kw -> 2013 return XTick(self.axes, 0, major=major, **tick_kw) 2014 /usr/local/lib/python3.6/dist-packages/matplotlib/axis.py in __init__(self, *args, **kwargs) 430 xdata=[0, 0], ydata=[0, 1], --> 431 transform=self.axes.get_xaxis_transform(which="grid"), 432 ) /usr/local/lib/python3.6/dist-packages/matplotlib/artist.py in set(self, **kwargs) 1087 """A property batch setter. Pass *kwargs* to set properties.""" -> 1088 kwargs = cbook.normalize_kwargs(kwargs, self) 1089 move_color_to_start = False /usr/local/lib/python3.6/dist-packages/matplotlib/cbook/deprecation.py in wrapper(*inner_args, **inner_kwargs) 410 **kwargs) --> 411 return func(*inner_args, **inner_kwargs) 412 /usr/local/lib/python3.6/dist-packages/matplotlib/cbook/deprecation.py in wrapper(*inner_args, **inner_kwargs) 385 def wrapper(*inner_args, **inner_kwargs): --> 386 arguments = signature.bind(*inner_args, **inner_kwargs).arguments 387 if is_varargs and arguments.get(name): /usr/lib/python3.6/inspect.py in bind(*args, **kwargs) 2996 """ -> 2997 return args[0]._bind(args[1:], kwargs) 2998 /usr/lib/python3.6/inspect.py in _bind(self, args, kwargs, partial) 2934 -> 2935 if param.name in kwargs: 2936 raise TypeError( /usr/lib/python3.6/inspect.py in _bind(self, args, kwargs, partial) 2934 -> 2935 if param.name in kwargs: 2936 raise TypeError( /usr/lib/python3.6/bdb.py in trace_dispatch(self, frame, event, arg) 50 if event == 'line': ---> 51 return self.dispatch_line(frame) 52 if event == 'call': /usr/lib/python3.6/bdb.py in dispatch_line(self, frame) 68 if self.stop_here(frame) or self.break_here(frame): ---> 69 self.user_line(frame) 70 if self.quitting: raise BdbQuit /usr/local/lib/python3.6/dist-packages/nbtutor/ipython/debugger.py in user_line(self, frame) 62 """This function is called when we stop or break at this line.""" ---> 63 self.get_stack_data(frame, None, 'step_line') 64 /usr/local/lib/python3.6/dist-packages/nbtutor/ipython/debugger.py in get_stack_data(self, frame, traceback, event_type) 122 stack_data.add(frame, lineno, event_type, user_locals) --> 123 heap_data.add(user_locals) 124 /usr/local/lib/python3.6/dist-packages/nbtutor/ipython/history.py in add(self, filtered_locals) 298 for obj in filtered_locals.values(): --> 299 self._add(obj) 300 /usr/local/lib/python3.6/dist-packages/nbtutor/ipython/history.py in _add(self, obj, **kwargs) 289 elif type_info[0] == 'array': --> 290 self._add_array_object(obj, type_info, **kwargs) 291 elif type_info[0] == 'class' or type_info[0].endswith('instance'): /usr/local/lib/python3.6/dist-packages/nbtutor/ipython/history.py in _add_array_object(self, obj, type_info, **kwargs) 149 if not self.options.expand_arrays and obj.ndim == 1: --> 150 self._add_array_data_object(obj, type_info, **kwargs) 151 self.data[-1]['type'] = '{} ({})'.format(type_name, obj.dtype) /usr/local/lib/python3.6/dist-packages/nbtutor/ipython/history.py in _add_array_data_object(self, obj, type_info, **kwargs) 120 break --> 121 data_values.append(format(val, self.options)) 122 /usr/local/lib/python3.6/dist-packages/nbtutor/ipython/utils.py in format(obj, options) 118 if isinstance(obj, _types): --> 119 return fmtr(obj) 120 try: /usr/local/lib/python3.6/dist-packages/nbtutor/ipython/utils.py in <lambda>(x) 114 formatters = { --> 115 float_types: lambda x: '{:.{}g}'.format(x, options.digits), 116 } KeyboardInterrupt: During handling of the above exception, another exception occurred: BdbQuit Traceback (most recent call last) <ipython-input-16-72b13d736f51> in <module> ----> 1 get_ipython().run_cell_magic('nbtutor', '--reset --force --debug', '\nimport numpy as np\nimport matplotlib.pyplot as plt\n\nX = np.linspace(-10, 10, 40)\n\ndef f1(x): return np.cos(x**2)\ndef f2(x): return np.sin(x**2)\ndef f3(x): return f1(x)+f2(x) \nY1 = f1(X)\nY2 = f2(X)\nY3 = f3(X)\n\nwith plt.xkcd():\n plt.figure(figsize=(14,8))\n plt.plot(X, Y1, "+-r", label="$f_1(x)$", ms=10, lw=3)\n #plt.plot(X, Y2, "o-b", label="$f_2(x)$", ms=10, lw=3)\n #plt.plot(X, Y3, "d-y", label="$f_3(x)$", ms=10, lw=3)\n plt.legend()\n #plt.xlabel("Axe $x$")\n #plt.ylabel("Axe $y$")\n plt.title("Trois fonctions de la variable réelle")\n plt.show()\n') /usr/local/lib/python3.6/dist-packages/IPython/core/interactiveshell.py in run_cell_magic(self, magic_name, line, cell) 2369 with self.builtin_trap: 2370 args = (magic_arg_s, cell) -> 2371 result = fn(*args, **kwargs) 2372 return result 2373 <decorator-gen-126> in nbtutor(self, line, cell) /usr/local/lib/python3.6/dist-packages/IPython/core/magic.py in <lambda>(f, *a, **k) 185 # but it's overkill for just that one bit of state. 186 def magic_deco(arg): --> 187 call = lambda f, *a, **k: f(*a, **k) 188 189 if callable(arg): /usr/local/lib/python3.6/dist-packages/nbtutor/ipython/magic.py in nbtutor(self, line, cell) 68 69 bdb = Bdb(self.shell, opts) ---> 70 bdb.run_cell(cell) 71 72 self.shell.run_cell(cell) /usr/local/lib/python3.6/dist-packages/nbtutor/ipython/debugger.py in run_cell(self, cell) 50 self.code_error = True 51 if self.options.debug: ---> 52 raise BdbQuit 53 finally: 54 self.finalize() BdbQuit:
Je suspectais que ça n'allait pas marcher avec nbtutor puisque ça ne marchait pas avant !! puisque nbtutor a les mêmes limitations que tutormagic !
Mais ça marche pour du numpy pur !
Et ça marche aussi pour des petits codes utilisant Matplotlib, mais très vite mon CPU prend 100%, ça rame, je ne vais pas chercher davantage.
%%nbtutor --reset --force
import numpy as np
X = np.linspace(-10, 10, 400)
def f1(x): return np.cos(x**2)
def f2(x): return np.sin(x**2)
def f3(x): return f1(x)+f2(x)
Y1 = f1(X)
Y2 = f2(X)
Y3 = f3(X)
Y = [Y1, Y2, Y3]
for i, y in enumerate(Y):
for f in [np.min, np.max, np.mean]:
name_f = str(f.__name__)
print(f"{name_f} of Y{i+1}, result = {f(y)}")
Cet exemple fonctionne !
Je vais écrire un programme C qui fait un petit truc intéressant, et essayer les choses suivantes :
tutormagic
! Je pense que ça devrait fonctionner, car c'est une iframe vers PythonTutor.com, qui supporte le C (version C11 ou C17) avec gcc ;nbtutor
! Si ça marche, c'est que ça utilise le serveur PythonTutor sans le montrer, sinon ça ne devrait pas marcher./* Cette cellule est écrite en langage C, il ne faut pas l'exécuter avec le kernel Python */
/* Installez le kernel https://github.com/brendan-rius/jupyter-c-kernel */
#include <stdio.h>
#define MIN_I 1
#define MAX_I 10
#define MIN_POWER 1
#define MAX_POWER 5
unsigned long power(int n, unsigned long acc, unsigned long x) {
if (n == 0) { return acc; }
if (n % 2 == 0) { return power(n/2, acc, x*x); }
else { return power((n-1)/2, acc * x, x*x); }
}
int main(void) {
for (int i = MIN_I; i <= MAX_I; i++) {
printf("\n- Pour i = %i :", i);
for (int n = MIN_POWER; n <= MAX_POWER; ++n) {
printf("\n\ti^%i = %li", n, power(n, 1, i));
}
}
}
- Pour i = 1 : i^1 = 1 i^2 = 1 i^3 = 1 i^4 = 1 i^5 = 1 - Pour i = 2 : i^1 = 2 i^2 = 4 i^3 = 8 i^4 = 16 i^5 = 32 - Pour i = 3 : i^1 = 3 i^2 = 9 i^3 = 27 i^4 = 81 i^5 = 243 - Pour i = 4 : i^1 = 4 i^2 = 16 i^3 = 64 i^4 = 256 i^5 = 1024 - Pour i = 5 : i^1 = 5 i^2 = 25 i^3 = 125 i^4 = 625 i^5 = 3125 - Pour i = 6 : i^1 = 6 i^2 = 36 i^3 = 216 i^4 = 1296 i^5 = 7776 - Pour i = 7 : i^1 = 7 i^2 = 49 i^3 = 343 i^4 = 2401 i^5 = 16807 - Pour i = 8 : i^1 = 8 i^2 = 64 i^3 = 512 i^4 = 4096 i^5 = 32768 - Pour i = 9 : i^1 = 9 i^2 = 81 i^3 = 729 i^4 = 6561 i^5 = 59049 - Pour i = 10 : i^1 = 10 i^2 = 100 i^3 = 1000 i^4 = 10000 i^5 = 100000
tutormagic
¶Alors évidemment, la "cell magic" %%tutor
ne marche pas depuis le kernel C, mais en repassant au kernel Python 3, on peut faire :
%reload_ext tutormagic
%%tutor --lang c --height 600
/* Cette cellule est écrite en langage C, il ne faut pas l'exécuter avec le kernel Python */
/* Installez le kernel https://github.com/brendan-rius/jupyter-c-kernel */
#include <stdio.h>
#define MIN_I 1
#define MAX_I 10
#define MIN_POWER 1
#define MAX_POWER 5
unsigned long power(int n, unsigned long acc, unsigned long x) {
if (n == 0) { return acc; }
if (n % 2 == 0) { return power(n/2, acc, x*x); }
else { return power((n-1)/2, acc * x, x*x); }
}
int main(void) {
for (int i = MIN_I; i <= MAX_I; i++) {
printf("\n- Pour i = %i :", i);
for (int n = MIN_POWER; n <= MAX_POWER; ++n) {
printf("\n\ti^%i = %li", n, power(n, 1, i));
}
}
}
Conclusion ?
tutormagic marche TROP bien pour du C !
Il faudrait ajouter le OCaml... Cf discussion initiée ici !
nbtutor
¶Il n'y a pas pour l'instant la possibilité d'utiliser nbtutor pour d'autres langages, mais ce ticket en parle.
tutormagic
¶IPython.display(...)
, donc pas moyen de les garder... et même en "Save notebook widget state" ça ne fonctionne pas, je m'en doutais ;nbtutor
¶tutormagic
!Est-ce que ça marche bien depuis un slide live RISE ? Je pense que oui ! Oui !
tutormagic
, ça marche sans aucun problème ! Pour Python et C !nbtutor
, ça marche mais les contrôles ne s'affichent pas... j'ai ouvert ce ticket, à voir si le développeur du projet répondra à mes questions !Qu'est-ce que ça donnerait une fois le notebook transformé en page web HTML statique ?
Et si je transforme en PDF, ça donne quoi ?
Plus de notebooks ? Merci d'avoir lu ! Allez voir d'autres notebooks intéressants dans ma collection qui grandit depuis 2016. Plein de notebooks en Python niveau L2/L3 mais aussi en OCaml et Python niveau agrégation, et bien plus ! En Java, Rust, Julia, Bash, GNUPlot et GNU Octave et des démonstrations d'extensions peu connues mais super intéressantes !