6.3. La bibliothèque standard

Le language Python comprend un certain nombre de fonctions prédéfinies, telles que abs() qui renvoie la valeur absolue d’un nombre ou sum() qui calcule la somme des éléments d’une liste. Ces fonctions, qui ne seraient pas très difficiles à écrire soi-même, existent pour simplifier la vie au programmeur. En effet, réécrire des fonctions aussi élémentaires pour chaque programme serait une grande perte de temps. De plus, toutes les fonctions utiles et à portée générale ne sont pas triviales à écrire, et encore moins de manière efficace. Les méthodes de compression, par exemple, mettent en œuvre des algorithmes complexes. Les fonctions natives de Python, qui sont limitées en nombre, ne suffisent donc plus dès que l’on souhaite écrire des programmes complexes sans devoir réinventer la roue.

Le concept de bibliothèque ou paquet (« library » ou « package » en anglais) permet de répondre à ce besoin. Une bibliothèque se compose de plusieurs modules, chacun contenant des définitions de fonctions ou/et de classes (objets personnalisés, un sujet que nous n’avons pas encore abordé). Tout développeur peut créer et distribuer des bibliothèques. Il en existe donc des centaines de milliers à disposition dans l’index de paquets Python PyPI, généralement spécialisées dans des domaines particuliers. Nous nous concentrons ici sur la bibliothèque standard qui est fournie directement avec Python et permet de grandement étendre les fonctionnalités du language.

6.3.1. Importer des modules

Comme mentionné plus haut, une bibliothèque se compose de modules. Pour utiliser les fonctions définies dans un module, il faut importer celui-ci en tête de programme grâce au mot-clé import.

import module

Les fonctions du module peuvent ensuite être appelées en les précédant du nom du module à l’aide de la notation pointée.

module.function()

Nous allons parcourir rapidement les fonctionnalités de quelques modules parmi les plus fréquemment utilisés. Pas souci de concision, nous n’ajouterons pas systématiquement les directives d’import dans les extraits de code illustrant ces fonctionnalités.

6.3.2. Le module math

Le module math contient un grand nombre de fonctions mathématiques simples et avancées, ainsi que quelques constantes utiles.

On peut importer le module math dans le REPL afin de pouvoir l’utiliser durant la session en cours.

>>> import math

Une fois importé, toutes les fonctionnalités du module sont à disposition, dont l’une des plus importantes : sa documentation en ligne.

>>> help(math)

La page d’aide du module énumère l’intégralité des fonctions et des constantes disponibles, accompagnées d’une description. Nous allons passer en revue certaines d’entre elles.

Les constantes mathématiques

La valeur approximative de plusieurs quelques mathématiques peut être obtenue via le module math.

>>> math.e
2.718281828459045
>>> math.pi
3.141592653589793
>>> math.tau
6.283185307179586

Les fonctions trigonométriques

Le module math fournit les fonctions trigonométriques communes sin(x), cos(x) et tan(x), ainsi que les fonctions inverses asin(x), acos(x) et atan(x). Les contreparties hyperboliques de ces fonctions portent le même nom suivi de la lettre h.

Important

Les fonctions trigonométriques opèrent sur des valeurs en radians ! Il faut donc au besoin convertir les angles de degrés à radians et vice-versa, à l’aide de la formule classique

\[rad = \frac{\pi \times deg}{180}\]

ou en utilisant les fonctions math.radians(deg) et math.degrees(rad) qui convertissent respectivement les degrés en radians et les radians en degrés.

Les constantes étant des approximations, les résultats sont rarement exacts et mènent régulièrement à de minuscules erreurs d’arrondi.

>>> math.cos(math.radians(60))
0.5000000000000001

Il est néanmoins possible d’arrondir précisément un nombre décimal à l’aide de la fonction round(), que nous avons déjà vue, en spécifiant un nombre souhaité de décimales au moyen d’un second paramètre.

>>> round(math.cos(math.radians(60)), 6)
0.5

Parties entières

Les fonctions math.ceil(x) et math.floor(x) renvoient respectivement la partie entière supérieure et inférieure d’un nombre x.

>>> math.ceil(math.e) == math.floor(math.pi)
True

Fonctions exponentielle et logarithmique

La fonction math.exp(x) calcule le résultat de \(e^x\). Pour obtenir le logarithme de base quelconque d’un nombre x, on peut utiliser la fonction math.log(x, base). Cependant, les logarithmes de base 2 et 10 étant très communs, ils peuvent directement être calculés à l’aide des fonctions math.log2(x) et math.log10(x).

>>> math.exp(1)
2.718281828459045
>>> math.log2(1024)
10.0

Exponentiation

La fonction math.pow(x, y) calcule \(x^y\). Il s’agit d’une alternative à la notation x ** y.

La racine carrée du nombre x peut être calculée au moyen de la fonction math.sqrt(x).

>>> math.pow(27, 1/3)
3.0

Pour les autre racines, on utilisera la formule suivante :

\[\sqrt[y]{x} = x^{\frac{1}{y}}\]

Factorielle

La fonction math.factorial(x) permet de calculer la factorielle \(x!\) d’un nombre x.

>>> math.factorial(2021) / math.factorial(2020)
2021.0

Somme et produit

Les deux fonctions math.fsum(iterable) et math.prod(iterable) calculent respectivement la somme et le produit des éléments de la collection passée en paramètre.

>>> l = (3, math.pi, -1, math.sqrt(11), 0.1)
>>> math.fsum(l)
8.558217443945193
>>> math.prod(l)
-3.1258452228282936

6.3.3. Le module random

Il arrive régulièrement que l’on souhaite utiliser le hasard pour simuler un comportement aléatoire dans les programmes, comme par exemple dans les jeux vidéos, lors de simulations ou en cryptographie. Le module random contient à cet effet des fonctions basées sur des algorithmes de génération de nombres pseudo-aléatoires.

Note

Ces nombres sont dits pseudo-aléatoires car ils ne sont pas parfaitement aléatoires, et ne sont par conséquent pas idéaux pour des applications comme la génération de clés cryptographiques de haute sécurité. Ils sont cependant générés de manière à sembler le plus aléatoires possible, et offrent l’avantage de pouvoir être obtenus facilement et à la demande. L’aléatoire parfait est quant à lui très difficile à obtenir.

random.random()

La fonction la plus simple du module est random.random(), qui renvoie un nombre aléatoire entre 0 inclus et 1 exclu, c’est-à-dire dans l’intervalle \([0, 1[\). C’est cette fonction qui est à la source de l’aléatoire pour les autres fonctionnalités du module.

>>> random.random()
0.05396495032371362
>>> random.random()
0.49196318491872193
>>> random.random()
0.17343301338828787

Le nombre généré est rarement utilisé tel quel. Il est généralement transformé en une valeur plus facilement utilisable, comme un entier positif que l’on obtient à l’aide d’une multiplication et d’un arrondi.

Considérons l’exemple d’un personnage pour laquelle nous aimerions choisir aléatoirement une taille comprise entre 150 et 200 centimètres avec une précision d’une décimale selon une distribution linéaire.

def choose_height():
    return 150 + round(random.random() * 50, 1)
>>> choose_height()
172.3
>>> choose_height()
199.3
>>> choose_height()
167.8
>>> choose_height()
153.3

La conversion d’un nombre décimal en entier à l’aide de int() est une manière commode d’éliminer la partie décimale. Ici, nous multiplions d’abord le nombre aléatoire par 50 pour obtenir un nombre dans l’intervalle \([0, 50[\). Puis nous ne conservons qu’une décimale du nombre à l’aide de la fonction round().

random.randint()

Nous pouvons généraliser notre solution pour créer une fonction qui génère un nombre entier entre deux bornes (comprises).

def choose_int_between(start, stop):
    return start + int(random.random() * (end - start + 1))
>>> choose_int_between(0, 1000)
605
>>> choose_int_between(0, 1000)
47
>>> choose_int_between(-13, 2)
-7
>>> choose_int_between(-13, 2)
2

Dans ce code, comme nous ne conservons pas de partie décimale, nous pouvons utiliser une simple conversion du nombre aléatoire en entier à l’aide de la fonction int().

La fonction random.randint(a, b) remplit précisément ce rôle en générant des nombres aléatoires entiers compris entre a et b inclus.

>>> random.randint(0, 1000)
557
>>> random.randint(-13, 2)
-4

random.choice()

Lorsqu’il faut choisir un élément quelconque d’une liste, nous pouvons très simplement tirer au hasard un indice de la collection puis y accéder de la manière suivante.

>>> t = ('a', 'b', 'c', 'd', 'e', 'f')
>>> index = random.randint(0, len(t) - 1)
>>> index
1
>>> t[index]
'b'

Cette opération courante est simplifiée par la fonction random.choice(iterable), qui choisit et renvoie un élément de la collection au hasard.

>>> random.choice(t)
'd'
>>> random.choice(t)
'b'

random.shuffle()

La fonction random.shuffle(l) mélange la liste l.

>>> abc = list("abcdefgh")
>>> random.shuffle(abc)
>>> abc
['d', 'h', 'b', 'c', 'e', 'a', 'f', 'g']

6.3.4. Le module time

Nous terminons ce tour d’horizon de la bibliothèque standard par une rapide présentation de quelques fonctionnalités utiles du module time, qui permet de mesurer et manipuler le temps écoulé.

time.time()

La fonction time.time() renvoie le nombre de secondes écoulées depuis le 1er janvier 1970, 00:00 (UTC). Il s’agit d’un point de référence commun à la plupart des systèmes d’exploitation et utilisé dans de nombreux langages de programmation.

>>> time.time()
744501600.5719344

Ce nombre est très utile pour mesurer les durées. Écrivons par exemple un programme qui mesure le nombre de seconde entre chaque entrée de l’utilisateur.

while True:
    t = time.time()
    input("Appuyez sur ENTER")
    print(round(time.time() - t, 3))
Appuyez sur ENTER
1.224
Appuyez sur ENTER
0.411
Appuyez sur ENTER
2.959
...

On peut convertir cette valeur en une date correspondante à l’aide de la fonction time.ctime(n).

>>> time.ctime(0)
'Thu Jan  1 01:00:00 1970'
>>> time.ctime(time.time())
'Wed Aug 18 17:04:17 2021'

time.sleep()

La fonction time.sleep(n) met l’exécution du programme en pause pour une durée de n secondes. Combiné à la syntaxe particulière de print() suivante (qui réécrit par dessus la ligne précédente), on peut ainsi créer une petite animation dans la console.

frames = ['\\', '|', '/',  '-']

while True:
    t = int(time.time());
    print(f"{time.ctime(t)} {frames[t % 4]}", end = '\r')
    time.sleep(1)

Nous vous laissons le soin d’exécuter ce code pour en observer le résultat (n’oubliez pas d’importer le module time au début de votre programme) !