Python pour les maths, au lycée

Ce document est disponible en fichier html statique (ni exécutable, ni modifiable) et en ipynb (à ouvrir avec Jupyter, par exemple ici : https://try.jupyter.org/).

Le matin

Présentation de Python, de l’esprit qui l’habite.

  1. Projection de ce diaporama (qui reprend le texte ci-dessous juqu’aux premiers échantillons de code à tester) ;
  2. et explication de tous les échantillons de codes contenus dans ce présent document (voir ci-dessous).

L’après-midi

Travail autour de la suite de Syracuse, écriture d’un programme qui :

  1. demande un nombre entier,
  2. affiche les termes de la suite de Syracuse de cet entier, jusqu’au premier 1,
  3. obtention et affichage du temps de vol, de l’altitude maximale, du temps de vol en altitude.

Présentation des modules utiles au lycée à partir de ces documents.

Dans le style pythonique

En 1991 Guido van Rossum, un universitaire hollandais, crée Python un langage alors assez nouveau et assez différent des C, C++, C#, Java, PHP... même s’il y a bien plus étrange encore, comme Forth, Haskell, Lisp... (voir un joli diagramme sur l’histoire des principaux langages de programmation ou voir esolangs.org pour des langages aussi fous les uns que les autres).

Conçu pour l’industrie, pour que les programmeurs soient productifs (donc pas pour l’enseignement), c’est un langage vaste. Il a des côtés que l’on qualifiera de simplets et d’autres de rusés.

  • simplet, qui vise à l’uniformisation, que tout le monde peut comprendre, comme MicroAlg
  • rusé, qui permet des finesses, qui rend le langage concis ou malléable

Un exemple de code « rusé » :

In [1]:
[x**2 for x in range(10) if not x%2]
Out[1]:
[0, 4, 16, 36, 64]

…proche de la notation mathématique $\left\{x^2; x \in \left[\left[0, 9\right]\right]~\text{tel que}~x~\text{est impair} \right\}$.

Les pythonistas codent en suivant certains principes, décrits dans le PEP 20, dont voici un extrait :

  • Beautiful is better than ugly.
  • Explicit is better than implicit.
  • Simple is better than complex.
  • Complex is better than complicated.
  • Flat is better than nested.
  • Sparse is better than dense.
  • Readability counts.
  • Special cases aren't special enough to break the rules.
  • Although practicality beats purity. ...

La traduction (tirée de cette page) :

  • Le beau est préférable au laid.
  • L'explicite est préférable à l'implicite.
  • Le simple est préférable au complexe.
  • Le complexe est préférable au compliqué.
  • Le plat est préférable à l'imbriqué.
    Moins littéralement, du code trop imbriqué (par exemple une boucle imbriquée dans une boucle imbriquée dans une boucle…) est plus difficile à lire.
  • L'aéré est préférable au compact.
  • La lisibilité compte.
  • Les cas particuliers ne sont pas suffisamment particuliers pour casser la règle.
  • Même si l'aspect pratique doit prendre le pas sur la pureté.
    Moins littéralement, il est difficile de faire un code à la fois [« pratique »] et « pur ».

On peut même aller jusqu’à dire qu’il y a une sorte de mystique de la programmation Python.

Au niveau de la forme, c’est le PEP 8.

On peut dire aussi que Python est impératif, orienté objet, et un peu fonctionnel. Il n’est cependant pas concatenatif.

C’est ce langage que l’inspection générale a choisi dans le double but :

  1. d’enseigner l’algorithmique ;
  2. d’enseigner la programmation (notez que M.Chéno a déconseillé l’utilisation d’EduPython, il faut utiliser une version récente de Python 3).

Cependant, difficile de concilier la pythonicité et l’algorithmique de niveau lycée.

Quelques idées pour travailler avec Python:

  1. Ce document a été créé avec Jupyter (https://try.jupyter.org/).
  2. IDLE est fourni avec la distribution officielle de Python (écrit en Python). Il permet de programmer en mode interactif (l’invite est >>>) ou de rédiger des programmes pour les exécuter ensuite.
  3. Brython est une version de Python entièrement codée en JavaScript:
    1. https://brython.info/tests/console.html
    2. https://brython.info/tests/editor.html
    3. http://profgra.org/lycee/brython.html
  4. Diverses applications mobiles permettent de travailler avec Python.

Premières valeurs, premiers types

Un cadre qui suit l’indication In [X] est un échantillon de code Python. La « valeur » de cet échantillon après exécution est indiquée par Out[X]. Parfois, un échantillon n’aura pas de valeur, mais déclenchera un affichage.

In [2]:
# le dièse indique que la fin de la ligne n'est pas interprétée (commentaire)
# penser à laisser une espace après #, c'est plus lisible
In [3]:
42  # un nombre
Out[3]:
42
In [4]:
print(42)  # l'affichage d'un nombre (pas de Out)
42
In [5]:
42.666  # . est le séparateur décimal
Out[5]:
42.666
In [6]:
.1  # le zéro est facultatif
Out[6]:
0.1
In [7]:
# la virgule est le séparateur des valeurs d'un uplet (couple, triplet, quadruplet…)
# « tuple » en anglais
# parenthèses facultatives, un premier exemple de ruse dans Python
1, 42, 666
Out[7]:
(1, 42, 666)
In [8]:
# Ne pas confondre les tuples avec les listes !
[1, 42, 666]
Out[8]:
[1, 42, 666]
In [9]:
# liste à un élément
[42]
Out[9]:
[42]
In [10]:
# tuple à un élément
42,
Out[10]:
(42,)
In [11]:
# sinon
(42)
Out[11]:
42
In [12]:
1+2*3  # un exemple de calcul
Out[12]:
7
In [13]:
# différents opérateurs pour la division
7/3, 7//3, 7%3
Out[13]:
(2.3333333333333335, 2, 1)

Attention aux nombres à virgule flottante !!!

Voir l’article Wikipedia correspondant et l’exemple 2 page 13 de ce poly.

Des modules existent pour travailler avec une précision exacte : decimal et fractions.

In [14]:
2**3, 10**1000  # puissances d'un nombre, ici on voit la précision arbitraire des entiers (tester 10**1000+0.1)
Out[14]:
(8,
 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000)
In [15]:
1 == 2, 1 != 2, 1 < 2, 1 > 2, 1 <= 2, 1 >= 2  # comparaisons de nombres
Out[15]:
(False, True, True, False, True, False)
In [16]:
True and (False or not True)  # booléens et opérateurs
Out[16]:
False
In [17]:
"bonjour", 'salut', '', 'un guillemet double: "', "un guillemet simple: '", ""  # les chaînes de caractères
Out[17]:
('bonjour',
 'salut',
 '',
 'un guillemet double: "',
 "un guillemet simple: '",
 '')
In [18]:
"""Les triples guillemets (simples ' ou doubles "),
permettent de taper une chaîne de caractères
qui s’étend sur plusieurs lignes et/ou qui contient des guillemets."""
Out[18]:
'Les triples guillemets (simples \' ou doubles "),\npermettent de taper une chaîne de caractères\nqui s’étend sur plusieurs lignes et/ou qui contient des guillemets.'
In [19]:
print("bonjour")
bonjour
In [20]:
"un guillemet double: \""  # l’antislash est un caractère d’échappement
Out[20]:
'un guillemet double: "'
In [21]:
# la commande 'type' retourne des commandes qui peuvent convertir
type(0), type(0.1), type(True), type('du texte'), type(None)
Out[21]:
(int, float, bool, str, NoneType)
In [22]:
int(0.5), float(0), bool(0.5), str(0.5)
Out[22]:
(0, 0.0, True, '0.5')
In [23]:
type(0)(1234.5)
Out[23]:
1234
In [24]:
# La conversion en booléen permet de comprendre comment seront interprétés certains tests
[bool(x) for x in [0, 0.1, '', '0', None, [0]]]
Out[24]:
[False, True, False, True, False, True]

Affectation et affichage

L’affectation se déroule en trois temps :

  1. Python lit l’expression à droite du = ;
  2. Python en obtient une valeur ;
  3. Python stocke cette valeur dans la variable dont le nom est à gauche du signe =.
In [25]:
x = 1  # rien ne s’affiche, rien n’est retourné
In [26]:
x = 1
print(x)
x = x + 1  # n’a pas le même sens qu’en maths ! MicroAlg peut aider ici.
print(x)
x += 1     # raccourci pour l’incrémentation, peu explicite
print(x)
1
2
3

Avec Python l’affectation peut travailler sur plusieurs variables en même temps :

In [27]:
x, y = 42, 666  # ne pas oublier que ce sont des tuples
print(x)
print(y)
42
666

Encore plus fort, les sous-structures sont respectées :

In [28]:
(x, [a, b], y, l) = [1, (2, 3), 4, (5, 6, 7)]
print(a)
print(b)
print(x)
print(y)
print(l)
2
3
1
4
(5, 6, 7)

Ce genre de possibilité ruine notre exercice classique de l’échange des valeurs de deux variables.

In [29]:
a, b = 42, 666
print(a,b)  # notez l’affichage de deux valeurs (voir la doc de print)
a, b = b, a
print(a,b)
42 666
666 42

L’affectation est souple : pas besoin de déclaration préalable, pas besoin de respecter le type :

In [30]:
x = 1
print(x)
x = "bonjour"
print(x)
1
bonjour

Mais les variables sont tout de même typées :

In [31]:
x, y = 1, "bonjour"
# x + y   # déclenche l’erreur TypeError: unsupported operand type(s) for +: 'int' and 'str'

Programmation et notation objet

C’est hors programme pour nos élèves, mais important pour comprendre le sens du point.

Avec Python, toutes les valeurs sont des objets (alors qu’avec Java, certains types sont dits primitifs, comme les entiers).

Un objet possède :

  • des attributs, c’est-à-dire des valeurs qui lui sont attachées ;
  • des méthodes, c’est-à-dire des actions qui lui sont attachées.

C’est une façon décentralisée d’organiser le code d’un gros programme. Les valeurs sont en quelque sorte responsables de leur devenir.

Les attributs et méthodes sont prédéfinis pour les types de valeurs rencontrées ici, mais vous pourriez créer vos objets, voire vos modèles d’objets.

In [32]:
class chat: "Ceci est un chat !"  # un nouveau type d’objet: chat
c = chat()                        # c est une variable de type chat
c.nom = "Niny"                    # attribut (valeur de type chaîne de caractères)
c.cri = lambda : print("miaou")   # méthode (lambda crée des commandes à la volée)
# utilisation
print(c)
print(type(c))
print(c.nom)  # la notation «point» permet d’accéder à un attribut,
c.cri()       # ou à une méthode.
<__main__.chat object at 0x7fadf0035588>
<class '__main__.chat'>
Niny
miaou
In [33]:
def f(x):
    return 2*x
print(f(3))

g = lambda x: 3*x
print(g(7))
6
21

Tous les objets de Python ont des attributs et méthodes spéciaux, préfixés et suffixés par un double underscore, donc de la forme __truc__.

In [34]:
x = 1
print(x.__doc__)
int(x=0) -> integer
int(x, base=10) -> integer

Convert a number or string to an integer, or return 0 if no arguments
are given.  If x is a number, return x.__int__().  For floating point
numbers, this truncates towards zero.

If x is not a number or if base is given, then x must be a string,
bytes, or bytearray instance representing an integer literal in the
given base.  The literal can be preceded by '+' or '-' and be surrounded
by whitespace.  The base defaults to 10.  Valid bases are 0 and 2-36.
Base 0 means to interpret the base from the string as an integer literal.
>>> int('0b100', base=0)
4
In [35]:
print(c.__doc__)
Ceci est un chat !
In [36]:
help(c)  # demande d’aide sur l’objet
Help on chat in module __main__ object:

class chat(builtins.object)
 |  Ceci est un chat !
 |  
 |  Data descriptors defined here:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)

In [37]:
dir(c)  # demande de la liste des attributs et méthodes
Out[37]:
['__class__',
 '__delattr__',
 '__dict__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__gt__',
 '__hash__',
 '__init__',
 '__le__',
 '__lt__',
 '__module__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__',
 'cri',
 'nom']

À vous d’explorer les types principaux à coups de help(1), dir(str), ou toute autre combinaison de help, dir appelée avec une valeur ou un type.

Pour illustrer la malléabilité du langage, sachez qu’il est possible avec Python de donner du sens à la somme de deux chats, en définissant la méthode __add__ pour nos chats.

Modules

Les modules sont des objets qui sont chargés dans le langage à la demande, au cours de l’exécution du programme (généralement au début).

In [38]:
# print(math)  # déclenche l’erreur NameError: name 'math' is not defined
In [39]:
import math
print(math)
help(math)
<module 'math' (built-in)>
Help on built-in module math:

NAME
    math

DESCRIPTION
    This module is always available.  It provides access to the
    mathematical functions defined by the C standard.

FUNCTIONS
    acos(...)
        acos(x)
        
        Return the arc cosine (measured in radians) of x.
    
    acosh(...)
        acosh(x)
        
        Return the inverse hyperbolic cosine of x.
    
    asin(...)
        asin(x)
        
        Return the arc sine (measured in radians) of x.
    
    asinh(...)
        asinh(x)
        
        Return the inverse hyperbolic sine of x.
    
    atan(...)
        atan(x)
        
        Return the arc tangent (measured in radians) of x.
    
    atan2(...)
        atan2(y, x)
        
        Return the arc tangent (measured in radians) of y/x.
        Unlike atan(y/x), the signs of both x and y are considered.
    
    atanh(...)
        atanh(x)
        
        Return the inverse hyperbolic tangent of x.
    
    ceil(...)
        ceil(x)
        
        Return the ceiling of x as an Integral.
        This is the smallest integer >= x.
    
    copysign(...)
        copysign(x, y)
        
        Return a float with the magnitude (absolute value) of x but the sign 
        of y. On platforms that support signed zeros, copysign(1.0, -0.0) 
        returns -1.0.
    
    cos(...)
        cos(x)
        
        Return the cosine of x (measured in radians).
    
    cosh(...)
        cosh(x)
        
        Return the hyperbolic cosine of x.
    
    degrees(...)
        degrees(x)
        
        Convert angle x from radians to degrees.
    
    erf(...)
        erf(x)
        
        Error function at x.
    
    erfc(...)
        erfc(x)
        
        Complementary error function at x.
    
    exp(...)
        exp(x)
        
        Return e raised to the power of x.
    
    expm1(...)
        expm1(x)
        
        Return exp(x)-1.
        This function avoids the loss of precision involved in the direct evaluation of exp(x)-1 for small x.
    
    fabs(...)
        fabs(x)
        
        Return the absolute value of the float x.
    
    factorial(...)
        factorial(x) -> Integral
        
        Find x!. Raise a ValueError if x is negative or non-integral.
    
    floor(...)
        floor(x)
        
        Return the floor of x as an Integral.
        This is the largest integer <= x.
    
    fmod(...)
        fmod(x, y)
        
        Return fmod(x, y), according to platform C.  x % y may differ.
    
    frexp(...)
        frexp(x)
        
        Return the mantissa and exponent of x, as pair (m, e).
        m is a float and e is an int, such that x = m * 2.**e.
        If x is 0, m and e are both 0.  Else 0.5 <= abs(m) < 1.0.
    
    fsum(...)
        fsum(iterable)
        
        Return an accurate floating point sum of values in the iterable.
        Assumes IEEE-754 floating point arithmetic.
    
    gamma(...)
        gamma(x)
        
        Gamma function at x.
    
    gcd(...)
        gcd(x, y) -> int
        greatest common divisor of x and y
    
    hypot(...)
        hypot(x, y)
        
        Return the Euclidean distance, sqrt(x*x + y*y).
    
    isclose(...)
        isclose(a, b, *, rel_tol=1e-09, abs_tol=0.0) -> bool
        
        Determine whether two floating point numbers are close in value.
        
           rel_tol
               maximum difference for being considered "close", relative to the
               magnitude of the input values
            abs_tol
               maximum difference for being considered "close", regardless of the
               magnitude of the input values
        
        Return True if a is close in value to b, and False otherwise.
        
        For the values to be considered close, the difference between them
        must be smaller than at least one of the tolerances.
        
        -inf, inf and NaN behave similarly to the IEEE 754 Standard.  That
        is, NaN is not close to anything, even itself.  inf and -inf are
        only close to themselves.
    
    isfinite(...)
        isfinite(x) -> bool
        
        Return True if x is neither an infinity nor a NaN, and False otherwise.
    
    isinf(...)
        isinf(x) -> bool
        
        Return True if x is a positive or negative infinity, and False otherwise.
    
    isnan(...)
        isnan(x) -> bool
        
        Return True if x is a NaN (not a number), and False otherwise.
    
    ldexp(...)
        ldexp(x, i)
        
        Return x * (2**i).
    
    lgamma(...)
        lgamma(x)
        
        Natural logarithm of absolute value of Gamma function at x.
    
    log(...)
        log(x[, base])
        
        Return the logarithm of x to the given base.
        If the base not specified, returns the natural logarithm (base e) of x.
    
    log10(...)
        log10(x)
        
        Return the base 10 logarithm of x.
    
    log1p(...)
        log1p(x)
        
        Return the natural logarithm of 1+x (base e).
        The result is computed in a way which is accurate for x near zero.
    
    log2(...)
        log2(x)
        
        Return the base 2 logarithm of x.
    
    modf(...)
        modf(x)
        
        Return the fractional and integer parts of x.  Both results carry the sign
        of x and are floats.
    
    pow(...)
        pow(x, y)
        
        Return x**y (x to the power of y).
    
    radians(...)
        radians(x)
        
        Convert angle x from degrees to radians.
    
    sin(...)
        sin(x)
        
        Return the sine of x (measured in radians).
    
    sinh(...)
        sinh(x)
        
        Return the hyperbolic sine of x.
    
    sqrt(...)
        sqrt(x)
        
        Return the square root of x.
    
    tan(...)
        tan(x)
        
        Return the tangent of x (measured in radians).
    
    tanh(...)
        tanh(x)
        
        Return the hyperbolic tangent of x.
    
    trunc(...)
        trunc(x:Real) -> Integral
        
        Truncates x to the nearest Integral toward 0. Uses the __trunc__ magic method.

DATA
    e = 2.718281828459045
    inf = inf
    nan = nan
    pi = 3.141592653589793

FILE
    (built-in)


Une fois chargé, le module peut être utilisé, ou plutôt ses attributs ou méthodes peuvent être utilisés :

In [40]:
import math
math.pi, math.sqrt(2)
Out[40]:
(3.141592653589793, 1.4142135623730951)

La commande import est multiforme et permet de se passer de la notation « point », d’importer au copmte-gouttes, ou de renommer un attribut ou une méthode.

In [42]:
from math import cos             # il suffira de taper cos(angle) plutôt que math.cos(angle)
from math import sqrt as racine  # on pourra taper racine(2) au lieu de math.sqrt(2)
from math import *               # évite d’utiliser math. pour tous les attributs et méthodes de math (déconseillé)

Les modules disponibles dans la bibliothèque standard (c’est-à-dire qui ne nécessitent pas d’installation supplémentaire) sont listés ici. Citons math, random, decimal, fractions, cmaths, turtle, tkinter.