Les nouvelles technologies pour l’enseignement des mathématiques
Intégration des TICE dans l’enseignement des mathématiques

MathémaTICE, première revue en ligne destinée à promouvoir les TICE à travers l’enseignement des mathématiques.

Recension de quelques livres sur Python en maths

À l’occasion de l’introduction de Python au lycée, quelques ouvrages relativement (ou très) récents sont recencés dans cet article.

Article mis en ligne le 29 juin 2017
dernière modification le 31 juillet 2022

par Alain Busser

4 livres seront recensés ici :

  • Visa pour la prépa, de Guillaume Connan, paru chez Dunod, ISBN 9782100746989
  • L’essentiel de l’informatique en prépa, de Frantz Barrault, paru chez Ellipses, ISBN 9782340011496
  • Python pour les jeunes débutants, de Frédéric Laroche, paru chez Ellipses, ISBN 9782340004580
  • Le petit Python, de Richard Gomez, paru chez Ellipses, ISBN 9782340017863.

Seul ce dernier vise plus ou moins explicitement le lycée, et c’est aussi le plus récent. On y fera donc particulièrement attention.

Article placé sous licence CC-By-SA

Rappel historique

  • En 2009, à l’occasion de programmes transitoires en 2nde, est introduite l’algorithmique en maths, sans préciser s’il convient, ou non, de la décliner sous forme de programmation. Quelques collègues, assez rares, choisissent la programmation, et choisissent le langage Python, par sa concision et sa présence sur toutes plateformes.
  • En 2011, le langage de programmation Python est choisi, comme langage unique [1], dans le supérieur, en particulier en classes préparatoires aux grandes écoles. Voici un exemple vu en IUT.
  • Guillaume Connan participe alors à la rédaction d’un ouvrage qui ne sera pas recensé ici, mais qui couvre déjà le sujet des mathématiques avec Python. Il est décrit ici.
  • De mon côté j’avais exploré la possibilité non seulement de faire des maths avec Python mais même de baser l’essentiel du cours de 2nde sur des programmes en Python, bien plus concis que ceux en Algobox.
  • Des ouvrages sont parus en ligne sur le sujet, notamment calcul mathématique avec Sage et Mathématiques avec Python et Ruby.
  • Dès 2012, Python est choisi assez fréquemment en ISN puis en ICN, notamment sur le Raspberry Pi qui a été conçu spécialement pour la programmation en Python [2] ;
  • En 2017, pour marquer la différence avec le collège où a été introduite la programmation visuelle, le nouveau programme de 2nde donne la part large à la programmation, et c’est, officieusement, Python qui est choisi.

Note : Les ouvrages les plus récents, en particulier le document d’accompagnement, utilisent de plus en plus la version 3 de Python. L’auteur de cette recension, que ce soit par nostalgie ou par rebellion, en est encore à la version 2 de Python. Les exemples de cet article seront peut-être à adapter pour les rendre Python3-compatibles. Il s’agit essentiellement de remplacer print sortie par print(sortie), l’instruction print de Python2 étant devenue une fonction lors du passage à Python3.

Pour tester les programmes de cet article, on peut le faire en ligne, en allant sur SofusPy, puis en cliquant sur « éditeur » et en copiant-collant les scripts Python dans la console qui apparaît alors.

Dgesco

Dans le futur document d’Éduscol, on lit cette définition d’une variable informatique :

Un modèle rigoureux de la variable informatique consiste à dire qu’une variable est une étiquette collée sur une boîte qui peut contenir différentes valeurs. Le contenu de chaque boîte varie au cours de l’exécution d’un programme (ce qui n’est pas le cas d’une variable mathématique). Quand l’ordinateur évalue une expression dans laquelle figure une variable, comme x+4, le résultat de l’évaluation est la somme du nombre contenu dans la boîte d’étiquette x et de 4.

Le livre d’ISN édité par Gilles Dowek semble plutôt dire que c’est la boîte qui est la variable, et pas seulement l’étiquette (laquelle est le nom de la variable). D’ailleurs il est étrange d’appeler « variable » l’étiquette, puisque celle-ci ne varie pas...

Également :

Cette utilisation de l’indentation est obligatoire.

Et non, justement, elle ne l’est pas ! On peut aussi utiliser le point-virgule et écrire des scripts Python en une seule ligne. Ce qui ne signifie nullement que l’idée soit bonne...

Le document est destiné à des utilisateurs de Python 3 et uniquement cette version, c’est un peu dommage ; de plus, voici comment est prévu le calcul d’une moyenne :

  1. def moyenne(serie):
  2.     n = len(serie)
  3.     s = 0
  4.     for x in serie:
  5.         s = s + x
  6.     return s/n

Télécharger

Cet exemple est un peu indécis :

  • Soit on estime que les élèves ont besoin d’apprendre comment on additionne les termes de la série, et dans ce cas on se demande pourquoi ils peuvent utiliser len au lieu de programmer aussi l’algorithme de comptage ;
  • Soit on se dit que la version avec variable de comptage est trop compliquée et dans ce cas pourquoi pas utiliser sum également ?

Voici la version longue :

  1. def moyenne(serie):
  2.     n = 0
  3.     s = 0
  4.     for x in serie:
  5.         s = s + x
  6.         n = n+1
  7. return s/n

Télécharger

Et la version courte :

  1. def moyenne(serie):
  2.     return sum(serie)/len(serie)

Télécharger

Connan

Visa pour la prépa est, a priori, un livre de maths (il est une sorte de résumé de ce qui a été vu avant le bac et servira après, assorti de compléments utiles, notamment sur la théorie de l’intégration). Mais, outre le fait qu’il comporte un chapitre spécifiquement dédié à la programmation, des exemples en Python parsèment l’ouvrage, ce qui le rend intéressant pour chercher au moins des exemples d’utilisation de Python en maths au lycée.

fonctions

Python est décrit comme langage « objet » mais en citant la possibilité de programmer « fonctionnel ». Effectivement, dans le domaine des fonctions, Python est très pédagogique, comme le montre l’aventure suivante, inspirée par la programmation Logo :

Saisi d’un doute soudain, un élève, devant sa console Python, souhaite savoir quel est le cube de 4. Il va donc dans la console, écrire

print cube(4)

Et il aura ce message d’erreur :

NameError: name 'cube' is not defined

Effectivement, on peut vérifier cette absence du cube dans Python, en lui demandant quelles sont les variables globales, par ce script :

print globals()

L’affichage montre assez peu de choses :

{'__builtins__': <module '__builtin__' (built-in)>, '__name__': '__main__', '__doc__': None, '__package__': None}

4 catégories :

  • builtins qui est un module de Python (les fonctions prédéfinies, apparemment le cube n’y est pas) ;
  • name qui est « main » (la fonction principale de Python, en effet il n’y a pour l’instant aucune autre fonction) ;
  • doc qui est vide ;
  • et package qui est vide aussi.

Comme en Logo, si le mot « cube » ne fait pas partie du vocabulaire de Python, on peut l’y rajouter, en définissant ce mot :

  1. def cube(x):
  2.         return x**3
  3.  
  4. print cube(4)
  5.  
  6. print globals()

Télécharger

On y apprend, outre le fait que le cube de 4 est 64, qu’il y a maintenant le nom « cube » dans le dictionnaire globals de Python :

64

{'cube': <function cube at 0xb748b48c>, '__builtins__': <module '__builtin__' (built-in)>, '__package__': None, '__name__': '__main__', '__doc__': None}

(au passage on rappelle la syntaxe des exposants en Python : Un double astérisque)

Ainsi, pour que la fonction cube soit connue de Python, on a dû la définir, ce qui s’est fait en lui donnant un nom. Le fait que ce nom soit en français aide d’ailleurs à la compréhension des programmes Python qui utiliseront cette fonction. Par exemple, pour le calcul du volume d’une boule (en important un autre nom, depuis le module math) :

  1. from math import pi
  2.  
  3. def cube(x):
  4.         return x**3
  5.  
  6. def volume(rayon):
  7.         return 4/3*pi*cube(rayon)
  8.  
  9. print volume(15)

Télécharger

En fait, Guillaume Connan privilégie (à part dans le dernier chapitre) une autre approche : Définir une variable cube, et affecter à celle-ci une fonction :

  1. cube = lambda x: x**3
  2.  
  3. print cube(4)
  4.  
  5. print globals()

Télécharger

Cette fois-ci, cube n’est plus une fonction, mais une variable de type lambda function :

64
{'cube': <function <lambda> at 0xb748248c>, '__builtins__': <module '__builtin__' (built-in)>, '__package__': None, '__name__': '__main__', '__doc__': None}

Cette approche est pratique en analyse (la dérivation par exemple s’applique à une fonction) et est favorisée par l’utilisation dans l’ouvrage, de SymPy, qui peut même « lambdafier » une fonction ! Mais c’est ici peut-être que l’objectif de Guillaume Connan diffère de celui de la 2nde, et il n’est peut-être pas prudent d’utiliser dès l’entrée en lycée le concept de lambda-calcul !

Bien que pas destiné à des élèves de 2nde, l’ouvrage de Guillaume Connan peut servir ne serait-ce que par son côté « SymPy par l’exemple ».

Ensembles

Un problème avec SymPy est que sa fonction solve, très pratique, donne une liste de solutions, et pas un ensemble de solutions. Pour résoudre par exemple une équation du second degré, on peut programmer une fonction sd (comme « second degré »), qui accepte a, b et c en entrée, et renvoie l’ensemble S des solutions de l’équation. La première ligne de cette fonction empêche d’essayer de résoudre une équation du premier degré avec une fonction prévue pour le second degré :

  1. from sets import *
  2.  
  3. def sd(a,b,c):
  4.         assert a != 0
  5.         S = set()
  6.         Delta = b**2-4*a*c
  7.         if Delta >= 0:
  8.                 r = Delta**0.5
  9.                 S.add((-b-r)/(2*a))
  10.                 S.add((-b+r)/(2*a))
  11.         return S
  12.  
  13. print sd(1,-2,1)

Télécharger

L’ensemble S renvoyé contient 0, 1 ou 2 nombres selon le signe de Delta. Et c’est bien un ensemble de solutions qui est renvoyé, et pas une liste de solutions qui est affichée. Ceci dit, la version de l’ouvrage, basée sur SymPy, est plus générale, puisque la distinction entre premier et second degrés n’y est pas nécessaire, et que même au cas où le discriminant est négatif, les solutions sont affichées.

Les sacs de Python

Les dictionnaires de Python sont un bon moyen de modéliser les fonctions d’un ensemble fini (les clés du dictionnaire) vers un ensemble fini (les valeurs du dictionnaire). Les simulations probabilistes permettent de construire des exemples de multiensembles (ou sacs) sous forme de tableaux d’effectifs : À chaque issue possible d’un évènement, est associé un nombre, qui est le nombre de fois où cette issue a été atteinte. Par exemple pour le lancer de deux dés, les sommes peuvent rapidement être constituées en liste, avec ce script :

  1. from random import randint
  2.  
  3. liste = [randint(1,6)+randint(1,6) for n in range(1000)]
  4.  
  5. print liste

Télécharger

Seulement une liste de 1000 nombres, c’est assez peu lisible. Alors on la transforme en dictionnaire par comptage des occurences :

  1. from random import randint
  2. from collections import Counter
  3.  
  4. liste = [randint(1,6)+randint(1,6) for n in range(1000)]
  5.  
  6. effectifs = Counter(liste)
  7.  
  8. print effectifs

Télécharger

Le résultat est tout de même plus lisible :

Counter({7: 181, 8: 140, 6: 118, 5: 113, 9: 106, 4: 97, 11: 82, 10: 63, 3: 47, 2: 31, 12: 22})

On peut dessiner cette fonction sous forme d’un graphe orienté, avec ce script :

  1. # -*- coding: utf-8 -*-
  2.  
  3. from random import randint
  4. from collections import Counter
  5.  
  6. liste = [randint(1,6)+randint(1,6) for n in range(1000)]
  7.  
  8. effectifs = Counter(liste)
  9.  
  10. for n in range(2,13):
  11.         print n," ⟶ ",effectifs[n]

Télécharger

On obtient alors ce graphe orienté :

2  ⟶  26
3  ⟶  56
4  ⟶  83
5  ⟶  120
6  ⟶  148
7  ⟶  173
8  ⟶  144
9  ⟶  89
10  ⟶  80
11  ⟶  54
12  ⟶  27

Le module graphviz présente l’avantage, en considérant tout le diagramme sagittal comme un graphe bipartite, de ne pas dessiner deux fois le même élément de l’ensemble d’arrivée :

  1. from graphviz import Digraph
  2. from collections import Counter
  3. from random import *
  4. liste = [randint(1,6)+randint(1,6) for n in range(1000)]
  5. effectifs = Counter(liste)
  6. g = Digraph(format='png')
  7. for k in effectifs:
  8.         g.node(str(k),str(k),shape='box3d',style='filled',fillcolor='cyan')
  9.         g.node(str(effectifs[k]),str(effectifs[k]),shape='doublecircle',style='filled',fillcolor='yellow')
  10.         g.edge(str(k),str(effectifs[k]),color='red',rankdir='LR')
  11.  
  12.  
  13. g.render('graphe1',view=True)

Télécharger

Bien entendu, on peut préférer la représentation sous forme d’un tableau :

2 3 4 5 6 7 8 9 10 11 12
27 56 90 112 128 173 153 107 71 58 25

Celle-ci est assez facile à obtenir en Python, soit directement, soit en enregistrant les résultats dans un fichier csv ou autre.

Le livre de Guillaume Connan peut servir à préparer des séquences Python en lycée, par les exemples qu’il contient et qui peuvent donner des idées d’activités. Le seul problème étant qu’il manque un peu de géométrie, mais c’est semble-t-il une conséquence des programmes de prépa...

Barrault

L’essentiel de l’informatique en prépa, comme son titre l’indique, n’est pas spécialement destiné à l’usage de Python en cours de maths au lycée. En fait les premiers et les derniers chapitres du livre seraient plus utiles en cours d’ICN ou d’ISN qu’en maths. Cependant, les chapitres consacrés à l’analyse et à l’algèbre linéaire comprennent des exemples suffisamment courts pour servir de base à des exercices de niveau lycée.

Comme Guillaume Connan (voir onglet précédent), Franz Barrault préfère les lambda-fonctions aux définitions de fonctions.

générateur

L’exemple de la suite de Collatz est archi-classique (historique, faisant appel à une boucle et un test, objet d’un problème ouvert) mais Frantz Barrault en fait un usage intéressant, en démarrant par la suite elle-même, puis en étudiant le temps de vol et l’altitude [3]. Mais le fait que la fonction (définie par morceaux) à itérer s’appelle next, donne envie d’essayer de considérer la suite comme un itérateur à la place. Après tout ça peut être un exemple d’utilisation d’un itérateur. Le code (qui fait ce qu’il ne faut pas : Construire une liste à partir de l’itérateur) est celui-ci :

  1. def collatz(n):
  2.         u = n
  3.         while u>1:
  4.                 if u%2==0:
  5.                         u /= 2
  6.                 else:
  7.                         u *= 3
  8.                         u += 1
  9.                 yield u
  10.  
  11. print list(collatz(65))

Télécharger

Il affiche la suite :

[196, 98, 49, 148, 74, 37, 112, 56, 28, 14, 7, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1]

Et on peut la représenter graphiquement, chercher sa moyenne et sa médiane, etc. Disons que c’est un changement de point de vue que permet l’itérateur.

algorithmique

Alors que Guillaume Connan (voir onglet précédent) utilise le module timeit (spécialement dédié à cet effet) pour mesurer les temps de calcul, Frantz Barrault n’utilise que time, en enregistrant la valeur du temps dans une variable, avant et après l’appel à la fonction à mesurer. C’est plus rapide mais moins fiable (parfois l’ordinateur passe du temps à autre chose et ça fausse la mesure). Frantz Barrault mesure divers algorithmes de calcul intégral par ce biais, mais on peut essayer avec d’autres fonctions de niveau moins « prépa ». Par exemple, si on veut évaluer combien de temps il faut pour mélanger n cartes, à l’aide du mélange de Fisher-Yates qui est utilisé par Python, on peut tester le script suivant :

  1. from time import *
  2. from random import *
  3. import matplotlib.pyplot as plt
  4.  
  5. def melange_chrono(n):
  6.         t = time()
  7.         shuffle(list(range(n)))
  8.         return time()-t
  9.  
  10. temps = [melange_chrono(n) for n in range(1,1001)]
  11.  
  12. plt.plot(range(1,1001),temps,'b.')
  13. plt.show()

Télécharger

En omettant les valeurs anormales, on conjecture une complexité en O(n) :

Le module timeit utilisé par Guillaume Connan, par contre, donne un résultat statistique obtenu par répétition des mesures de chronométrage, et pas seulement un résultat de mesure ; elle résiste donc mieux aux valeurs anormales.

Il est à remarquer qu’en 2017, tous les sujets d’informatique des concours aux grandes écoles, ainsi que le sujet d’informatique du Capes de maths, comportaient des questions sur la complexité, notamment des algorithmes de tri...

algèbre linéaire

Frantz Barrault fait le choix de NumPy (plutôt que SymPy vu dans l’onglet précédent) pour faire de l’algèbre linéaire. Les deux outils peuvent être investis dès la 2nde pour le cours de géométrie repérée. Cependant la géométrie plane peut être vue avec l’objet Vec2D du module turtle. Par exemple voilà comment on peut calculer les coordonnées d’un vecteur et d’un milieu, à l’aide de celles des points (qui sont en fait des Vec2D) :

  1. from turtle import *
  2. A = Vec2D(-3,2)
  3. B = Vec2D(5,3)
  4. print B-A
  5. print (A+B)*0.5

Télécharger

Les questions affines comme l’alignement et la colinéarité ne sont pas gérées automatiquement par ce module, mais on peut y remédier avec ce genre de script assez court :

  1. def determinant(u,v):
  2.         return u[0]*v[1]-u[1]*v[0]
  3.  
  4. def colineaires(u,v):
  5.         return determinant(u,v) == 0
  6.  
  7. def alignes(A,B,C):
  8.         return colineaires(B-A,C-A)

Télécharger

De plus, on peut appliquer une rotation à un vecteur (en degrés), calculer sa norme (avec abs(u)), et le produit scalaire de deux vecteurs se code simplement u*v ce qui permet d’aller assez rapidement vers la notion d’angle de vecteurs. Et la distance entre deux points A et B s’obtient par abs(B-A).

Comme l’ouvrage de l’onglet précédent, celui de Frantz Barrault peut inspirer des exemples au lycée, et montre, par les exemples, la syntaxe des bibliothèques utilisées en prépa. Toutefois, il est moins axé sur les maths et serait plus utile en ICN (notamment de par la présence de SQL) qu’en maths.

Laroche

Apprendre à programmer en Python est sous-titré pour jeunes débutants de 7 à 97 ans. C’est la borne inférieure qui surprend, au vu du contenu [4]. Le livre a son site compagnon dont le style évoque assez bien le style « bricolage sous Windows » que l’on voit tout au long du livre...

Programmation objet

À vrai dire l’auteur est décrit comme enseignant l’ISN depuis la création de cette matière, et il n’est pas certain qu’il aie eu les maths en tête lorsqu’il a rédigé ce qui ne semble être qu’une compilation de projets menés par ses élèves en ISN. L’idée est d’apprendre Python par la pratique. Mais alors, cette phrase surprend un peu de par son caractère péremptoire :

Il existe des pseudo langages encore plus simples d’accès ou faciles d’utilisation comme Scratch avec une interface très conviviale mais ne présentant pas autant d’intérêt que Python au niveau du potentiel d’activités possibles et surtout des capacités d’apprentissage.

Il faut dire que l’auteur semble allergique à la programmation objet et ne perçoit pas d’intérêt à démarrer la programmation par des robots style RUR-PLE ou la tortue de Python. Or, contrairement à Python, la programmation objet a déjà été utilisée avec succès, et avec un très jeune public, via Squeak et Etoys.

En plus, la programmation objet, il y en a quand même pas mal, avec les widgets de easyguifr, très utilisés dans le livre même lorsque la console aurait pu être utilisée, au risque de compliquer nettement les scripts.

Nim

Un exemple de restriction de choix technologique apparaît dans le chapitre sur « Nim » [5] ; voici un extrait du code :

  1. from __future__ import division
  2. import random as rd
  3. import easyguifr as eg
  4.  
  5. N=15
  6. fini=False
  7. joueur=0
  8. R=0
  9.  
  10. while not fini:
  11.     (...)
  12.         (...)
  13.     for k in range(max_bouton):
  14.         ch.append(str(k+1)+' allumette(s)')
  15.     resultat=''
  16.     (...)
  17.         resultat=eg.boite_bouton(msg='Joueur '+str(joueur+1)+',\nCombien d\'allumette(s) prends-tu ?\nIl en reste : '+str(N-R),titre="Marienbad"
  18.                    ,choix=ch, image=None, root=None)
  19.     if not fini:joueur=(joueur+1) %2

Télécharger

Ce script laisse penser que les fonctionnalités graphiques de « easyguifr » sont uniquement sollicitées pour les boutons à cliquer et qu’il n’y a pas de dessin des allumettes. Voici à quoi aurait pu ressembler la version en console qui ne fait pas appel aux widgets, mais qui du coup permet de voir tout le jeu sur la console :

  1. from random import *
  2.  
  3. def afficher(n):
  4.         print "Il reste {0} allumettes :".format(N)
  5.         print "|"*N
  6.  
  7. N = randint(12,48)
  8. afficher(N)
  9. while N > 0:
  10.         R = 0
  11.         while R not in [1,2,3]:
  12.                 R = int(raw_input("Choisis un nombre d'allumettes entre 1 et 3 :"))
  13.         N -= R
  14.         afficher(N)
  15.         if N <= 0:
  16.                 print "Bravo c'est toi qui gagnes !!!"
  17.                 break
  18.         else:
  19.                 R = min(N,randint(1,3))
  20.                 print "Je prends {0} allumette(s) :".format(R), "|"*R
  21.                 N -= R
  22.                 afficher(N)
  23.         if N <=0:
  24.                 print "C'est moi qui gagne !"
  25.                 break

Télécharger

Voici un exemple de partie dans la console :

  1. Il reste 16 allumettes :
  2. ||||||||||||||||
  3. Choisis un nombre d allumettes entre 1 et 3 :1
  4. Il reste 15 allumettes :
  5. |||||||||||||||
  6. Je prends 1 allumette : |
  7. Il reste 14 allumettes :
  8. ||||||||||||||
  9. Choisis un nombre d allumettes entre 1 et 3 :2
  10. Il reste 12 allumettes :
  11. ||||||||||||
  12. Je prends 1 allumette : |
  13. Il reste 11 allumettes :
  14. |||||||||||
  15. Choisis un nombre d allumettes entre 1 et 3 :3
  16. Il reste 8 allumettes :
  17. ||||||||
  18. Je prends 1 allumette : |
  19. Il reste 7 allumettes :
  20. |||||||
  21. Choisis un nombre d allumettes entre 1 et 3 :3
  22. Il reste 4 allumettes :
  23. ||||
  24. Je prends 1 allumette : |
  25. Il reste 3 allumettes :
  26. |||
  27. Choisis un nombre d allumettes entre 1 et 3 :3
  28. Il reste 0 allumette :
  29.  
  30. Bravo c'est toi qui gagnes !!!

Télécharger

On voit sur cet exemple que l’auteur ne profite pas systématiquement de la concision que permet Python et que, du coup, même sur des activités relativement classiques, le temps que prendra l’enfant à s’approprier le script sera long, surtout s’il faut en plus aborder des notions mathématiques (coordonnées, multiplication, division,...) ou informatiques (css, xml, client/serveur, media,...) nouvelles à l’occasion du chapitre.

Fourmis

L’auteur fait le choix, pour déterminer la direction où va une fourmi de Langton, de faire des tests répétitifs avec elif :

  1. def tourner_a_gauche (sens):
  2.     if sens == "NORD":
  3.         sens0="OUEST"
  4.     elif sens == "EST":
  5.         sens0="NORD"
  6.     elif sens == "SUD":
  7.         sens0="EST"
  8.     else: sens0="SUD"
  9.     return sens0
  10.  
  11. def tourner_a_droite (sens):
  12.     if sens == "NORD":
  13.         sens0="EST"
  14.     elif sens == "EST":
  15.         sens0="SUD"
  16.     elif sens == "SUD":
  17.         sens0="OUEST"
  18.     else: sens0="NORD"
  19.     return sens0

Télécharger

Or l’usage de tableaux permet de faire plus court (modulo 8 parce que l’écran du Sense Hat est très petit) :

  1. def gauche():
  2.         global direction
  3.         direction = (direction+1) % 4
  4.  
  5. def droite():
  6.         global direction
  7.         direction = (direction+3) % 4
  8.  
  9. def avance():
  10.         global direction, x,y
  11.         mettre_bleu(x,y,0)
  12.         x = (x+dx[direction]) % 8
  13.         y = (y+dy[direction]) % 8
  14.         mettre_bleu(x,y,255)
  15.  
  16. dx = [1,0,-1,0]
  17. dy = [0,1,0,-1]

Télécharger

Ici dx et dy sont les coordonnées du vecteur qu’on additionne à la position courante de la fourmi (elle-même un vecteur) pour avoir la position suivante. On objectera que l’addition des vecteurs n’est pas une notion facile pour des enfants. On contre-objectera que les tests multiples avec elif non plus, puisque l’enfant qui a des difficultés à se projeter dans un monde hypothétique (« if ») peut éprouver des difficultés supplémentaires à se métaprojeter dans un monde hypothétique du point de vue du premier monde hypothétique...

Ce livre, issu, du propre aveu de l’auteur, de l’observation d’élèves de terminale S en ISN, semble être adapté à l’enseignement de l’informatique en terminale et non à l’enseignement des maths avant la terminale. On peut y trouver des idées de projets mobilisant des notions de maths, mais pour ce faire il faut chercher dans les plus de 300 pages, l’ordre logique du livre n’étant adapté ni à l’utilisation de Python pour les maths, ni à une progression pédagogiquement axée sur les maths.

Gomez

Le petit Python n’est pas si petit que ça puisqu’il dépasse les 400 pages. Ce livre n’a pas vocation à être lu mais plutôt à être régulièrement consulté en cas de trou de mémoire sur la syntaxe de Python :

Le lecteur n’est pas obligé de lire les chapitres dans l’ordre, il peut aller directement à l’endroit où on trouve l’information cherchée.

Théorie des ensembles

Un exemple du dernier chapitre est exposé dès la page 31 : Il s’agit, dans le cadre de l’introspection de Python, d’afficher toutes les fonctions de Python concernant les fractions [6]. Pour savoir ce que peut faire une fraction, on utilise dir :

  1. import fractions
  2. L = dir(fractions)
  3. print L

Télécharger

On en voit relativement (comparé à d’autres modules) peu :

['Decimal', 'Fraction', 'Rational', '_RATIONAL_FORMAT', '__all__', '__builtins__', '__doc__', '__file__', '__name__', '__package__', 'division', 'gcd', 'math', 'numbers', 'operator', 're']

Pour ne pas afficher (donc ne pas garder) les fonctions qui ne sont pas propres aux fractions (leurs noms sont entourés de « doubles underscores »), l’auteur propose une description de liste en compréhension (on ne garde que les noms qui ne commencent pas par un « underscore ») :

  1. import fractions
  2. L = dir(fractions)
  3. L = [x for x in L if x[0] != "_"]
  4. print L

Télécharger

On y voit mieux :

['Decimal', 'Fraction', 'Rational', 'division', 'gcd', 'math', 'numbers', 'operator', 're']

Mais il reste encore les définitions de classes, dont le nom commence par une majuscule. Pour ne pas les afficher, on pourrait faire

  1. import fractions
  2. L = dir(fractions)
  3. L = [x for x in L if x[0] != "_"]
  4. L = [x for x in L if x == x.lower()]
  5. print L

Télécharger

ou plus simple [7] :

  1. import fractions
  2. L = dir(fractions)
  3. L = [x for x in L if x[0] != "_" and x.islower()]
  4. print L

Télécharger

(ne garder que les mots invariants par passage en minuscules)

Mais ce n’est pas le choix de l’auteur ! Il a préféré utiliser la théorie des ensembles, avec cet algorithme :

  • les fonctions de conversion de Python permettent, à l’aide d’un mot, d’obtenir l’ensemble de ses lettres ;
  • on peut calculer l’intersection de cet ensemble avec l’ensemble de toutes les lettres majuscules possibles ;
  • la vacuité de cette intersection caractérise les mots qui s’écrivent sans aucune majuscule :
  1. import fractions
  2.  
  3. majuscules = set("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
  4. def sc(nom):
  5.         return (majuscules & set(nom) == set())
  6.  
  7. L = dir(fractions)
  8. L = [x for x in L if x[0] != "_"]
  9. L = [x for x in L if sc(x)]
  10. print L

Télécharger

Les deux versions donnent le même résultat :

['division', 'gcd', 'math', 'numbers', 'operator', 're']

Mais celle de l’auteur est susceptible d’être réutilisée dans bien d’autres situations, en particulier en mathématiques. Et on peut envisager d’introduire les notions mathématiques d’ensemble, d’ensemble vide, et d’intersection, avec ce genre d’activité.

Pour savoir ce que signifient les mots « division », « gcd » etc on peut alors faire

  1. from fractions import *
  2. help(gcd)

Télécharger

Aliasing

Lorsqu’on fait

  1. L1 = list(range(5))
  2. print L1
  3. L1.append(8)
  4. print L1

Télécharger

on a

[0, 1, 2, 3, 4]
[0, 1, 2, 3, 4, 8]

ce qui est l’effet attendu (on a converti un « range » en liste puis ajouté un élément à cette liste). Mais si on fait

  1. L1 = list(range(5))
  2. print L1
  3. L2 = L1
  4. L2.append(8)
  5. print L1

Télécharger

on a encore

[0, 1, 2, 3, 4]
[0, 1, 2, 3, 4, 8]

ce qui n’est pas l’effet attendu : On voulait modifier L2, pas L1. C’est l’aliasing en Python, et Richard Gomez y consacre un chapitre entier ; grand merci à lui, nul doute que ça améliorera le sommeil de bien des collègues désirant faire des statistiques avec Python !

Une solution à ce problème serait de passer par les compréhensions :

  1. L1 = list(range(5))
  2. print L1
  3. L2 = [x for x in L1]
  4. L2.append(8)
  5. print L1

Télécharger

(ce nouveau script ne modifie pas L1) ; mais Richard Gomez en propose une autre :

  1. L1 = list(range(5))
  2. print L1
  3. L2 = L1.copy()
  4. L2.append(8)
  5. print L1

Télécharger

Bonnes et mauvaises habitudes

L’exemple de module « self made » que propose Richard Gomez est très mathématique :

  1. """
  2.    module monmodule.py, écrit par Richard
  3. """
  4.  
  5. def longueur(r):
  6.     return 3.14*2*r
  7. def aire(r):
  8.     return 3.14*r*r

Télécharger

L’exemple est bien choisi (ne serait-ce que parce qu’il permet de rappeler des formules importantes mais trop souvent oubliées) mais il entretient l’idée de plus en plus répandue chez les lycéens, que « pi égale trois quatorze » [8]. Certes, pour expliquer comment est fait un module, il est étrange d’importer d’emblée un autre module dans celui qu’on définit, mais peut-être aurait-on plus de rigueur avec cette variante :

  1. from math import pi
  2.  
  3. def longueur(r):
  4.     return pi*2*r
  5. def aire(r):
  6.     return pi*r**2

Télécharger

Ce qui est à craindre, c’est qu’un élève prenne vite de mauvaises habitudes (comme par exemple de ne pas mettre de docstring, comme je l’ai fait ci-dessus...) et aie du mal à s’en défaire par la suite.

D’autres exemples figurent dans le chapitre sur les fichiers :

  • Une boucle sans fin très classiquement programmée par
  1. while 1:
  2.     ....

Télécharger

Le fait que cette boucle soit sans fin ne se voit pas très bien parce qu’il y a un implicite : La traduction automatique d’un entier en booléen fait que 1, entier non nul, est automatiquement traduit en « true ». Or, grammaticalement parlant, la phrase « tant que 1 » est dépourvue de sens parce que « tant que » devrait être suivi d’une proposition et non d’un nombre. Le « while true » souvent proposé à la place, est peu satisfaisant parce même si « true » peut être considéré comme une proposition, la traduction française « tant que vrai » est peu compréhensible. Des variantes plus satisfaisantes seraient

  1. while 2+2==4:
  2. while 1==1:
  3. while 1>0:
  4. while 1!=0

Télécharger

etc.

La première proposition se traduit en français par « tant que 2+2 est égal à 4 », traduction plus satisfaisante parce qu’on y perçoit mieux le caractère éternel de la boucle. Évidemment, si Python possédait le mot « forever », la question ne se poserait pas...

  • Une fonction existe, dont le nom suggère le caractère booléen, et qui se termine selon le cas par return 0 ou return 1 : Ce codage, inspiré par le langage C, est peu propice à une bonne compréhension des booléens chez les lycéens, il serait peut-être plus pertinent lorsqu’on teste une existence, d’en faire explicitement un booléen avec return False et return True.

Polya

Le document d’accompagnement suggère une modélisation un peu complexe pour étudier l’urne de Polya :

  1. import random
  2. def urnesPolya(r,b,etapes):
  3.         assert(r+b>=1)
  4.         L=[r/(r+b)]
  5.         for i in range(etapes):
  6.                 n = random.randint(1,r+b)
  7.                 if n<=r:
  8.                         r,b = r+1,b
  9.                 else:
  10.                         r,b = r,b+1
  11.                 L.append(r/(r+b))

Télécharger

La description de la fonction choice du module random donne envie de modéliser un peu plus simplement cette urne [9] :

  1. from random import *
  2. def urnePolya(r,b,etapes):
  3.         assert(r+b>=1)
  4.         urne = ['r']*r+['b']*b
  5.         for i in range(etapes):
  6.                 urne.append(choice(urne))
  7.         print urne.count('r')/len(urne)

Télécharger

Le urne.append(choice(urne)) décrit mieux l’énoncé « on tire au hasard une boule, on la remet dans l’urne en ajoutant une nouvelle boule de même couleur ».

Unicode

Le sous-titre de l’ouvrage est Aide-mémoire pour Python 3 : La version de Python est clairement définie, c’est la 3. Or la version 3 de Python accepte le format unicode sans broncher. Richard Gomez utilise pourtant peu l’unicode, en particulier dans les scripts et les affichages. C’est parfois surprenant, notamment avec les cartes : Parmi les couleurs, figure (!) le mot « Cœur » qui contient le caractère « œ », lequel est codé en unicode. Pourquoi pas écrire le mot « ♥ » à la place dans ce cas ? Certes les valeurs non numériques sont écrites avec des mots entiers comme « Valet » et les cartes sont représentées par des couples [10], il y a une forme de cohérence dans cette façon d’afficher. Cependant, cette activité de statistiques montre l’avantage d’une concision accrue lorsqu’il y a beaucoup de cartes.

Un autre exemple de non-utilisation d’unicode se trouve dans le chapitre sur le formatage de chaînes :

  1. # -*- coding: utf-8 -*-
  2. r = 3.5656434
  3. aire = 3.14*r*r
  4. patron = "Un disque de rayon {} cm possède une aire de {} cm2"
  5. print(patron.format(r,aire))

Télécharger

On remarque que la première ligne précise que l’encodage est utf-8 : Sinon il y aurait un message d’erreur à cause de la lettre accentuée de « possède » qui doit être codée en utf-8 (ou unicode). Alors, unicode pour unicode, pourquoi pas utiliser le caractère « exposant 2 » qui donnerait un meilleur affichage ?

  1. # -*- coding: utf-8 -*-
  2. r = 3.5656434
  3. aire = 3.14*r*r
  4. patron = "Un disque de rayon {} cm possède une aire de {} cm²"
  5. print(patron.format(r,aire))

Télécharger

L’affichage obtenu serait

Un disque de rayon 3.5656434 cm possède une aire de 39.9213723677 cm²

au lieu de

Un disque de rayon 3.5656434 cm possède une aire de 39.9213723677 cm2

Comme le montrent les exemples ci-dessus, le livre de Richard Gomez est plus une source d’informations qu’un manuel. Un exemple d’utilisation pourrait être celui-ci, absolument pas cité dans l’ouvrage :

Comment simuler en Python une loi de Xenakis ?

En feuilletant le chapitre sur les probabilités, on voit un dessin d’un triangle. On apprend alors que Python (ou plus précisément son module random) sait simuler des lois triangulaires. Le dessin suggère de prendre a et m égaux à 0 et b, à 1. D’où ce script :

  1. from random import *
  2. import matplotlib.pyplot as dessin
  3. stats = [triangular(0,1,0) for n in range(100000)]
  4. dessin.hist(stats,100)
  5. dessin.show()

Télécharger

Lequel script donne un histogramme bien triangulaire comme il se doit :

On peut dire que ce livre est une sorte de catalyseur pour créer des activités mathématiques avec Python 3.

Aucun des livres recensés ici n’est explicitement titré mathématiques en Seconde avec Python. Mais 3 d’entre eux peuvent se montrer utiles pour le nouveau programme de Seconde, parce que même sans le dire, il y a des maths dedans, et parce que les scripts sont souvent assez courts pour qu’on envisage de faire quelque chose de ce genre en classe.