1Modules, importation et le module math
Reecrire une racine carree ou un generateur aleatoire serait a la fois inutile et moins fiable que d'utiliser la bibliotheque standard. Savoir quel module contient quelle fonction, et connaitre leurs pieges, fait partie du bagage de base.
Un module est un fichier Python contenant des definitions reutilisables. Un paquet est un ensemble de modules. La bibliotheque standard est l'ensemble des modules livres avec Python.
Les quatre formes d'importation
import math # 1. le module entier
math.sqrt(2) # acces qualifie : math.sqrt
from math import sqrt, pi # 2. quelques noms
sqrt(2) # acces direct
import numpy as np # 3. avec un alias
np.array([1, 2])
from math import * # 4. TOUT : A EVITER
sqrt(2)
from ... import *
from math import *
from numpy import *
sqrt([1, 4, 9]) # laquelle des deux sqrt ? La derniere importee ecrase la premiere.
Cette forme pollue l'espace de noms, masque silencieusement des fonctions et rend le code illisible : on ne sait plus d'ou vient un nom. Les formes 1 a 3 sont les seules acceptables.
Toujours en tete de fichier, jamais dans une fonction (sauf cas
particuliers). Un import est peu couteux apres le premier appel — Python conserve les modules
charges dans sys.modules — mais le placer en tete documente les dependances.
Le module math
| Categorie | Fonctions |
|---|---|
| Constantes | pi, e, tau,
inf, nan |
| Puissances | sqrt, pow, exp,
log, log2, log10 |
| Trigonometrie | sin, cos, tan,
asin, acos, atan, atan2 |
| Hyperboliques | sinh, cosh, tanh |
| Arrondis | floor, ceil, trunc,
fabs |
| Arithmetique | factorial, gcd, lcm,
comb, perm, isqrt |
| Angles | degrees, radians, hypot |
| Tests | isclose, isnan, isinf,
isfinite |
from math import *
sqrt(16) # 4.0 (toujours un flottant)
16 ** 0.5 # 4.0 (equivalent)
isqrt(17) # 4 racine ENTIERE, exacte meme sur de grands entiers
log(100, 10) # 2.0 logarithme en base 10
log(e) # 1.0 logarithme NEPERIEN par defaut
factorial(20) # 2432902008176640000 (exact, entier)
gcd(12, 18) # 6
comb(10, 3) # 120 coefficient binomial exact
hypot(3, 4) # 5.0 sqrt(x^2 + y^2), sans debordement
sin(30) # -0.988... : 30 RADIANS, pas 30 degres !
sin(radians(30)) # 0.4999999999999999 ~ 1/2
degrees(pi) # 180.0
Et noter que \(\sin(\pi/6)\) ne donne pas exactement 0,5 : les flottants approchent \(\pi\).
Ne jamais tester sin(radians(30)) == 0.5, mais
isclose(sin(radians(30)), 0.5).
sqrt(-1) # ValueError: math domain error
log(0) # ValueError: math domain error
1 / 0 # ZeroDivisionError
1.0 / 0.0 # ZeroDivisionError (Python, contrairement a IEEE 754 pur)
float("inf") # inf : la valeur infinie existe et se manipule
inf - inf # nan : "not a number"
nan == nan # False ! nan n'est egal a rien, pas meme a lui-meme
isnan(nan) # True : la SEULE facon de tester
Le module cmath fournit les memes fonctions sur les complexes :
cmath.sqrt(-1) vaut 1j.
Precision et fonctions robustes
from math import expm1, log1p, fsum, isclose
exp(1e-10) - 1 # 1.000000082740371e-10 : perte de precision
expm1(1e-10) # 1.00000000005e-10 : exact
log(1 + 1e-15) # 1.1102230246251565e-15 : imprecis
log1p(1e-15) # 9.999999999999995e-16 : exact
sum([0.1] * 10) # 0.9999999999999999
fsum([0.1] * 10) # 1.0 : sommation compensee exacte
isclose(0.1 + 0.2, 0.3) # True
isclose(1e10, 1e10 + 1, rel_tol=1e-9) # True
0.1 + 0.2 == 0.3 # False
expm1(x) calcule \(e^x - 1\) et log1p(x) calcule \(\ln(1+x)\) sans
la cancellation catastrophique qui survient quand \(x\) est petit. Ces fonctions existent
precisement pour cela.
2Aleatoire, simulation et methode de Monte-Carlo
Le module random
Un ordinateur deterministe ne produit pas de hasard : il calcule une suite pseudo-aleatoire, entierement determinee par un etat initial appele graine (seed). Python utilise le Mersenne Twister, de periode \(2^{19937}-1\), dont les proprietes statistiques sont excellentes.
import random
random.random() # flottant uniforme dans [0, 1[
random.uniform(2, 5) # flottant uniforme dans [2, 5]
random.randint(1, 6) # entier dans [1, 6] : les DEUX bornes incluses
random.randrange(6) # entier dans [0, 5] : borne haute EXCLUE
random.choice([1, 2, 3]) # un element au hasard
random.choices([1,2,3], k=5) # 5 tirages AVEC remise
random.sample([1,2,3,4], 2) # 2 tirages SANS remise
random.shuffle(t) # melange t EN PLACE, renvoie None
random.gauss(0, 1) # loi normale N(0, 1)
random.expovariate(2) # loi exponentielle de parametre 2
randint(a, b) inclut \(b\), randrange(a, b) l'exclut. C'est une
incoherence historique de l'API : randint suit la convention mathematique,
randrange celle de range. Verifier a chaque usage.
shuffle modifie en place et renvoie None : ecrire
t = random.shuffle(t) detruit la liste.
random.seed(42)
print([random.randint(1, 100) for _ in range(5)]) # [82, 15, 4, 95, 36]
random.seed(42)
print([random.randint(1, 100) for _ in range(5)]) # [82, 15, 4, 95, 36] : IDENTIQUE
Fixer la graine rend une simulation reproductible : indispensable pour
deboguer, comparer deux algorithmes sur les memes donnees, ou publier un resultat verifiable.
Sans seed, Python initialise a partir de l'horloge et de l'entropie systeme.
random pour la securite
Le Mersenne Twister n'est pas cryptographiquement sur : observer 624 sorties consecutives
suffit a reconstituer l'etat interne et a predire toute la suite. Pour des mots de passe, des
jetons ou des cles, utiliser le module secrets.
Simulation et methode de Monte-Carlo
Estimer une quantite deterministe (une aire, une probabilite, une integrale) en simulant un grand nombre d'experiences aleatoires et en utilisant la loi des grands nombres. L'erreur decroit en \(O(1/\sqrt N)\) : gagner une decimale demande 100 fois plus de tirages.
import random
from math import sqrt, pi
def estime_pi(n):
"""Estime pi par tirages uniformes dans le carre [0,1]^2."""
dedans = 0
for _ in range(n):
x, y = random.random(), random.random()
if x*x + y*y <= 1:
dedans += 1
return 4 * dedans / n
random.seed(0)
for n in (10**3, 10**4, 10**5, 10**6):
e = estime_pi(n)
print(f"n = {n:>8} : pi ~ {e:.5f}, erreur {abs(e - pi):.5f}, "
f"1/sqrt(n) = {1/sqrt(n):.5f}")
n = 1000 : pi ~ 3.16800, erreur 0.02641, 1/sqrt(n) = 0.03162
n = 10000 : pi ~ 3.14840, erreur 0.00681, 1/sqrt(n) = 0.01000
n = 100000 : pi ~ 3.14172, erreur 0.00013, 1/sqrt(n) = 0.00316
n = 1000000 : pi ~ 3.14117, erreur 0.00042, 1/sqrt(n) = 0.00100
Justification. Le quart de disque a pour aire \(\pi/4\) et le carre unite pour aire 1. La probabilite qu'un point uniforme tombe dans le quart de disque vaut donc \(\pi/4\). Si \(X_i\) vaut 1 quand le \(i\)-ieme point est dedans, la loi des grands nombres donne \(\frac1n\sum X_i \to \pi/4\) presque surement. Le theoreme central limite precise l'erreur : \(\sqrt n\bigl(\frac{4}{n}\sum X_i - \pi\bigr)\) converge en loi vers une gaussienne, d'ou une erreur typique en \(\Theta(1/\sqrt n)\).
3Autres modules et ecriture d'un module
Autres modules utiles
| Module | Usage | Exemple |
|---|---|---|
statistics | statistiques descriptives | mean, median, stdev, mode |
fractions | rationnels exacts | Fraction(1,3) + Fraction(1,6) |
decimal | decimaux a precision arbitraire | calculs financiers |
itertools | combinatoire | permutations, combinations, product |
functools | outils fonctionnels | reduce, lru_cache |
collections | structures | Counter, deque, defaultdict |
time | mesure de temps | perf_counter |
cmath | fonctions complexes | cmath.sqrt(-1) |
import statistics as st
notes = [12, 15, 9, 18, 11, 15]
st.mean(notes) # 13.333333333333334
st.median(notes) # 13.5
st.mode(notes) # 15
st.stdev(notes) # 3.2659863237109041 (ecart-type d'ECHANTILLON, n-1)
st.pstdev(notes) # 2.9814426348136544 (ecart-type de POPULATION, n)
stdev divise par \(n-1\) (estimateur sans biais de la variance d'une population a
partir d'un echantillon), pstdev divise par \(n\) (ecart-type de la population
complete). Choisir selon que les donnees sont un echantillon ou la population entiere.
from fractions import Fraction
Fraction(1, 3) + Fraction(1, 6) # Fraction(1, 2) : EXACT
1/3 + 1/6 # 0.49999999999999994 : approche
Fraction(0.1) # Fraction(3602879701896397, 36028797018963968)
Fraction("0.1") # Fraction(1, 10) : depuis une CHAINE, exact
sum(Fraction(1, k) for k in range(1, 11)) # Fraction(7381, 2520) : H_10 exact
from itertools import permutations, combinations, product, accumulate
list(permutations([1,2,3])) # les 6 permutations
list(combinations([1,2,3,4], 2)) # les 6 paires
list(product([0,1], repeat=3)) # les 8 triplets binaires
list(accumulate([1,2,3,4])) # [1, 3, 6, 10] : sommes prefixes
Ecrire son propre module
# fichier geometrie.py
"""Fonctions de geometrie plane."""
from math import pi, hypot
def aire_disque(r):
"""Aire d'un disque de rayon r >= 0."""
return pi * r * r
def distance(a, b):
"""Distance euclidienne entre deux points (x, y)."""
return hypot(a[0] - b[0], a[1] - b[1])
if __name__ == "__main__":
# ce bloc n'est execute QUE si le fichier est lance directement,
# pas s'il est importe. Ideal pour les tests.
assert abs(aire_disque(1) - pi) < 1e-12
assert distance((0, 0), (3, 4)) == 5.0
print("tests de geometrie : ok")
# dans un autre fichier
import geometrie
geometrie.aire_disque(2)
from geometrie import distance
distance((0,0), (1,1))
if __name__ == "__main__":
Quand un fichier est execute directement, la variable __name__ vaut
"__main__" ; quand il est importe, elle vaut le nom du module. Ce test permet de
placer les tests et le programme principal dans le meme fichier que les fonctions, sans qu'ils
s'executent a l'import. C'est une convention universelle en Python.
L'essentiel du chapitre
- Importer avec
import m,from m import fouimport m as alias; jamaisfrom m import *. - Les fonctions trigonometriques travaillent en radians.
math.isclosepour comparer des flottants,math.isnanpour testernan.randint(a,b)inclut \(b\),randrange(a,b)l'exclut ;shufflemodifie en place.random.seed(k)rend une simulation reproductible.- Monte-Carlo : erreur en \(O(1/\sqrt N)\), donc convergence lente mais insensible a la dimension.
if __name__ == "__main__":pour separer bibliotheque et programme principal.