Vous êtes sur la page 1sur 14

Une introduction à Python 3

Olivier Gauthé

Sommaire
1 Introduction 2

2 Environnement 2
2.1 Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
2.2 Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
2.3 Linux et macOS . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
2.4 Utilisation de Spyder . . . . . . . . . . . . . . . . . . . . . . . . 3

3 Premier pas sur Python 3


3.1 Une calculatrice . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
3.2 La fonction print . . . . . . . . . . . . . . . . . . . . . . . . . . 4
3.3 Les variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
3.4 Les listes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
3.5 Les booléens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
3.6 Les fonctions prédéfinies . . . . . . . . . . . . . . . . . . . . . . 6

4 Les boucles 6

5 Les fonctions 8

6 Lire et écrire dans un fichier 9

7 Écrire un programme dans un fichier 10

8 Quelques éléments de calcul numérique 10

9 Le calcul formel avec SymPy 12


9.1 Pourquoi le calcul formel ? . . . . . . . . . . . . . . . . . . . . . 12
9.2 Premiers pas sur SymPy . . . . . . . . . . . . . . . . . . . . . . 12

1
1 Introduction
Ce tutoriel s’adresse à des étudiants qui n’ont jamais fait de Python au-
paravant. Le seul prérequis est une initiation élémentaire à la programmation
comprenant une introduction aux concepts de variable et de boucle. Il se veut
simple et facilement lisible et laisse de côté un grand nombre de concepts im-
portants en programmation ainsi que de nombreuses possibilités du langage
Python. Le lecteur qui souhaiterait aller plus loin est renvoyé à la documenta-
tion et aux tutoriels plus complets cités plus bas.
Python est un langage de programmation complet qui peut être utilisé pour
réaliser des programmes scientifique, mais aussi des jeux vidéo, des sites web,
des logiciels professionnels... Il offre donc bien plus de possibilités qu’un langage
comme MATLAB, restreint au calcul mathématique. Python recherche avant
tout la simplicité et la lisibilité, en particulier il est très proche du langage hu-
main et très facile à apprendre. Ce tutoriel se concentre sur la programmation
scientifique, pour laquelle Python dispose de bibliothèques très fournies.
L’ENS Ulm propose un très bon tutoriel pour Python scientifique, cepen-
dant ce tutoriel est en Python 2 ce qui change notamment la syntaxe de la
division entière et celle de la fonction print. L’auteur recommande également
le tutoriels d’Open Classrooms ainsi que celui de Tutorials Point (en anglais).
La documentation officielle de Python 3 se trouve ici. Celle de SymPy, le
module de calcul formel, se trouve là. Enfin, une documentation complète sur
la programmation scientifique en Python, notamment de SymPy se trouve ici.

2 Environnement
2.1 Installation
Pour être certain de disposer de tous les modules nécessaires et avoir une in-
terface unique, nous travaillerons avec la distribution Anaconda Python. Suivre
les instructions de la page de téléchargement pour l’installer sous Windows,
Linux ou macOS. Il n’est pas nécessaire de disposer des droits administrateurs.

2.2 Windows
Lancer le programme Python(x,y). Celui-ci propose de lancer Spyder ou
IPython en console, plus minimaliste. Lancer Spyder en cliquant sur l’icône du
serpent rouge.

2.3 Linux et macOS


Sur Linux ou macOS, on peut utiliser Python ou IPython (Interactive Py-
thon) directement dans la console, en rentrant python ou ipython dans un
terminal. Afin d’avoir la même interface quel que soit le système d’exploita-
tion, on va également utiliser Spyder. Lancer Spyder en rentrant spyder dans
un terminal.

2
Figure 1 – Aperçu de l’interface Spyder

2.4 Utilisation de Spyder


Spyder est un environnement de développement pour Python. Il fournit
une interface graphique avec une console en bas à droite, des informations
au-dessus et un script à gauche (voir figure 1). Avant tout chose, se pla-
cer dans un répertoire dédié : créer un nouveau répertoire avec l’explo-
rateur de fichier, puis se placer dans ce répertoire avec Spyder : le répertoire
courant de Spyder est affiché en haut à droite. Par défaut, ce répertoire est
$HOME/.config/spyder sur Linux.
On peut rentrer le code directement dans la console ou bien utiliser le
script de la partie gauche, le sauvegarder et l’exécuter avec la touche F5. Par
défaut, le script est sauvegardé dans le répertoire courant de Spyder, d’où
l’importance d’être dans un répertoire dédié. La console peut être Python
ou IPython, IPython se voulant plus interactif et plus facile d’accès pour le
débutant ; on peut créer une nouvelle console du type voulu avec un clic droit
sur l’onglet de la console. Dans tout ce qui suit, on utilisera la console Python
(d’où les >>>), mais le résultat serait le même avec une console IPython.

3 Premier pas sur Python


3.1 Une calculatrice
Tester les opérations de base dans la console :

>>> 1 + 1
>>> 2*3
>>> 3.14 + 2.72
>>> 1 + 3.14

3
>>> 1j*1j

On observe que le calcul entre nombres entiers est exact, par contre le
calcul entre nombres à virgule (en anglais float, pour virgule flottante) n’est
pas exact : on a une erreur d’arrondi. En Python 3, la division s’écrit / et
renvoie un float, même si les variables sont entières. La division euclidienne
s’écrit //, avec une barre double. Le reste s’obtient avec %, il est toujours
positif. Les puissances s’écrivent avec ** : ab → a**b ; les priorités opératoires
sont usuelles et les opérations se font de la gauche vers la droite.

>>> 10/3
>>> 10//3
>>> 10%3
>>> -1%2
>>> 1 + 2*3%4

3.2 La fonction print


La fonction print permet d’afficher un élément dans la console, elle est très
pratique pour afficher le déroulé d’un code au fur et à mesure. print prend
autant d’argument qu’on veut et les affiche les un à la suite des autres. Par
défaut, une espace sépare les différents arguments et un saut de ligne est ajouté
après le dernier argument. On peut changer le séparateur avec l’argument-clé
sep et le dernier caractère inséré avec l’argument-clé end.

>>> print(1, 2)
>>> print(1, "a", 2, sep=",")
>>> print(2*2, 3.14, end="")

3.3 Les variables


On assigne une variable avec le signe =. Le nom d’une variable est sensible
à la casse, aussi long qu’on le souhaite et peut contenir des chiffres en dehors
du premier caractère. Python 3 accepte les caractères accentués dans les noms
de variable, mais il est rarement opportun d’en utiliser.

>>> a = 1
>>> A = 2
>>> a + A

On peut assigner plusieurs variables sur une seule ligne, ou échanger deux
variables.

>>> a, b, c = 1, 2, 3
>>> print(a, b, c)
>>> a, b = b, a
>>> print(a, b)

4
Une variable Python a un type : int (nombre entier), float (nombre à
virgule), bool (booléen)... Celui-ci est dynamique et peut changer au cours du
programme. Utiliser des noms de variable logiques : on s’attend à ce
que x soit un réel, pas un entier !
>>> i = 5
>>> print(i)
>>> type(i)
>>> c = 1.0
>>> type(c)
>>> c = "Paul Sabatier"
>>> type(c)
L’opérateur += permet d’incrémenter une variable de manière simple et
compacte : i += k est identique à i = i + k. Les opérateurs +=, -=, *=, etc
sont définis de même.
>>> n = 1
>>> n += 3
>>> n
>>> n *= 2
>>> n
On peut effectuer certaines opérations entre variables de types différents,
en général tant que le résultat a un sens logique et prévisible la syntaxe est
autorisée.
>>> s1, s2 = "Paul", "Sabatier"
>>> n = 3
>>> x = 3.14
>>> n + True
>>> n*x
>>> s1 + " " + s2
>>> s1 + n # erreur
Pour une chaîne de caractères (str, en anglais string), on peut utiliser
indifféremment guillemets ", apostrophes ’, ou triples guillemets """. On peut
y insérer une valeur avec la syntaxe .format :
>>> print(’1 + 1 = {}’.format(1+1))
>>> a = 3
>>> print("""a^2 = {}, a*2 = {}""".format(a**2,a*2))

3.4 Les listes


Une liste Python s’écrit entre crochets []. Elle peut contenir des variables
de types différents :
>>> maListe = [1, 3.14, "spam", 2+2, "eggs"]
>>> print(maListe)
>>> type(maListe)

5
On accède à la longueur d’une liste avec la fonction len. On accède à un
élément donné avec l’opérateur [], avec l’indice de l’élément comme argument.
En Python, les indices vont de 0 à len(maListe) -1.

>>> len(maListe)
>>> print(maListe[0], maListe[1], maListe[2])
>>> print(maListe[len(maList)]) # erreur

On peut ajouter ou supprimer des éléments à une liste :

>>> maListe
>>> maListe.append("bacon")
>>> maListe += [7] # same as append
>>> maListe.remove("eggs")
>>> maListe

3.5 Les booléens


Un booléen est une variable qui vaut True ou False. On peut leur appliquer
les opérateurs and, or, not.

>>> b = True
>>> type(b)
>>> not b or (b and not b)

3.6 Les fonctions prédéfinies


Python dispose d’un certain nombre de fonctions prédéfinies :

>>> max(1,2)
>>> l = [1,2,3,4,5]
>>> print(max(l), min(l), sum(l))
>>> int(3.5)

Pour obtenir plus d’information sur une fonction ou une variable, on utilise
la fonction help, qui affiche l’aide de la fonction. On quitte cette aide avec la
touche q. L’aide en ligne reste plus exhaustive. Pour les variables, type permet
de retrouver le type. Enfin, la console IPython a également la syntaxe ?x, qui
marche aussi bien pour une fonction que pour une variable, et qui affiche dans
la console toutes les informations connues sur l’objet.

4 Les boucles
En programmation, on rajoute en général des espaces en début de ligne
pour marquer de manière visible les délimitations d’un bloc d’instruction : on
appelle ces espaces des indentations. En Python, l’indentation est obliga-
toire : le principe de base est que la syntaxe est fixée par l’indentation.
Tout nouveau bloc d’instruction nécessite une indentation supplémentaire et

6
ce bloc prend fin lorsque cette indentation disparaît. À la fin d’une série d’ins-
truction, en sortant du dernier niveau d’indentation, il faut passer une ligne.

La syntaxe de la boucle for est for x in E:, ne pas oublier les " :" !
Cette syntaxe est très générale et permissive : on peut itérer sur tout contenu
itérable, comme par exemple une liste ou une chaîne de caractères. Pour
une boucle sur des entiers, on utilise range pour construire l’ensemble des
nombres voulus et itérer dessus. La convention Python est l’intervalle ouvert :
range(m,n) contient les nombres de m à n − 1.
>>> for i in range(10):
... print(i)
...
>>> s = "Sabatier"
>>> for char in s:
... print(char, end=" ")
...
>>> maListe = [1, 3.14, 4, "eggs"]
>>> for x in maListe:
... print(x, end=" ")
...
On peut imbriquer des instructions et des boucles en rajoutant un niveau
d’indentation à chaque fois. On passe une ligne à la fin du dernier bloc d’ins-
truction.
>>> for i in range(3):
... for j in range(10):
... print("{} x {} = {}".format(i,j,i*j))
...
Les autres instructions ont une syntaxe semblable, toujours avec un :. La
syntaxe d’une boucle conditionnelle est if boolean:, les opérateurs >, <, >=,
<=, ==, != ont le résultat attendu. La syntaxe d’une boucle while est while
boolean:. Exemple de conditions imbriquées :
>>> for i in range(5):
... j = 0
... while j<6:
... if i + j == 2:
... print("{} + {} = 2".format(i,j))
... elif i + j == 3:
... print("{} + {} = 3".format(i,j))
... else:
... print(i, j, "ni 2 ni 3")
... j += 1
...
Le nombre d’espace utilisées pour l’indentation n’a pas d’importance, il faut
juste toujours utiliser le même nombre au sein d’un même bloc d’instruction.

7
5 Les fonctions
La syntaxe pour déclarer une fonction est def func(arg): Encore une fois,
on indente dans le corps de la fonction et on passe une ligne pour signaler la
fin du bloc d’instruction :
>>> def somme(i,j):
... return i+j
...
>>> somme(1,2)
Les fonctions sont surchargées par défaut : tant que les opérations utili-
sées ont un sens pour les types utilisés, la fonction marche avec les variables
données. On peut passer des arguments par défaut en rajoutant un =.
>>> somme("Paul ","Sabatier")
>>> def somme2(x,y=2):
... return x+y
...
>>> somme2(1,3)
>>> somme2(1)
On peut manipuler une fonction comme n’importe quelle variable (regarder
son type), et la prendre comme argument d’une autre fonction. Il faut par
contre toujours veiller à ce que les opérations aient un sens et que les arguments
soient les bons.
>>> type(somme)
>>> def g(x,y,f):
... return f(x,y) + x
...
>>> g(1,2,somme)
Python est très laxiste sur ce que peut faire une fonction : elle peut utiliser
une variable globale définie en dehors de la fonction, renvoyer des variables
de types différents selon les cas, renvoyer une ou plusieurs variables, ne rien
renvoyer... Cependant, par cohérence et pour la lisibilité, il convient de tou-
jours renvoyer le même nombre de valeurs avec le même type. Une fonction
s’arrête lorsqu’elle arrive à un return. Exemple :
>>> m = 3
>>> def f(n):
... if not n%3:
... return n+m
... if n%3 == 1:
... return 2*n+1
... if n%3 == 2 and n%2 == 1:
... return 2*n+2
... return 0
...

8
On peut également définir une fonction avec la syntaxe f = lambda arg:
result. Une lambda fonction a une syntaxe très simple et permet de définir
rapidement des fonctions. Il est conseillé de ne les utiliser que dans des cas très
simple : pas plus d’une ligne. La syntaxe def est en général plus claire.

>>> f = lambda x,y: x + y


>>> f(1,1)
>>> type(f)

Python autorise une fonction à modifier ses arguments. En général mieux


vaut éviter de le faire, le résultat est souvent contre-intuitif et rend le code
plus difficile à lire.

6 Lire et écrire dans un fichier


On utilise le mot-clé with et la fonction open. On passe le nom du fichier
et le mode de lecture comme argument à open et on ajoute un niveau d’inden-
tation : le fichier est accessible à l’intérieur de ce bloc d’instruction, toujours
défini par l’indentation. Python s’assure tout seul que le fichier est bien ouvert,
qu’il existe dans le cas d’une lecture et que tout se passe bien, en particulier
que dans tous les cas le fichier est bien fermé à la fin du bloc d’instruction. Par
défaut, les fichiers sont ouverts dans le répertoire courant, il faut donc veiller
à travailler dans un répertoire dédié (indiqué en haut à droite). On peut aussi
spécifier un chemin dans le nom du fichier.
Les actions possibles dans un fichier sont spécifiées par le mode d’ouverture
du fichier. Celles-ci sont :
— "r" read
— "w" write
— "a" append
— "b" binary

>>> with open("fichier1.txt","w") as myFile:


... myFile.write("Paul Sabatier\n")
... myFile.write("Toulouse {}\n".format(3))
...

Regarder avec un bloc note ce que contient le fichier. Comparer avec ce qui se
passe lorsqu’on enlève le \n. On peut bien sûr mettre une boucle à l’intérieur
du bloc with open et écrire un texte différent à chaque itération.
À la lecture d’un fichier, il y a beaucoup de méthodes possibles pour ré-
cupérer les données une fois le fichier ouvert, en voici quelques-unes, regarder
l’aide en ligne pour en trouver d’autres.

>>> with open(filename,"r") as myFile:


... line1 = myFile.readline() # read first line
... line2 = myFile.readline() # read second line
...

9
>>> print(line1, line2)
>>> with open(file2,"r") as myFile:
... for line in myFile: # read line by line
... print(line)
...
>>> with open(file3,"r") as myFile:
... data = myFile.readlines() # store the whole file
...

La dernière méthode permet de récupérer la totalité du contenu du fichier d’un


coup, mais il faut faire attention à ce que le fichier ne soit pas trop gros pour ne
pas saturer la mémoire. Pour sauvegarder de gros objets comme des tableaux
de nombre et les recharger directement dans l’environnement, regarder l’aide
en ligne du module pickle.

7 Écrire un programme dans un fichier


La console Python n’est pas un environnement pratique pour écrire de longs
programmes : si on fait une erreur à un endroit, on est obligé de réécrire tout
le bloc d’instructions depuis le début. Il est donc utile d’écrire un script dans
un fichier annexe. L’extension d’un fichier de code Python est .py.
Dans Spyder, il suffit d’utiliser l’éditeur de script à gauche. Par défaut le
script s’appelle temp.py et est sauvegardé dans le répertoire courant, mais
on peut le renommer et le sauvegarder comme on veut. Une fois le fichier
sauvegardé, on l’exécute dans Spyder en appuyant sur la touche F5.
Pour exécuter un script directement dans la console Linux, il faut se placer
dans le répertoire où se trouve le script et rentrer

python monScript.py

Pour rester dans Python une fois l’exécution du script terminée, on rajoute
le flag -i. Cela permet de rester dans l’environnement Python une fois le
programme terminé. Les variables et fonctions utilisées dans le script sont
toujours définies, comme si on avait juste copié collé les instructions dans la
console.

python -i monScript.py

8 Quelques éléments de calcul numérique


Pour faire du calcul numérique avec Python, on utilise le module NumPy
(Numeric Python). Le tutoriel officiel contient beaucoup d’informations sur les
nombreuses possibilités de NumPy, on se limitera ici à quelques fonctionnalités
simples. On appelle un module avec le mot-clé import et on utilise le mot-clé
as pour raccourcir le nom à appeler ensuite :

>>> import numpy as np

10
Le module NumPy fournit une liste de fonctions usuelles en mathéma-
tiques : sqrt, exp, cos, sin, log, log2 , log10 , floor, ceil, round... Quelques exemples :

>>> print np.exp(1.0), np.exp(np.pi*1j), np.sin(np.pi/2)


>>> print np.log(np.exp(1)), np.log2(8.0), np.log10(100)
>>> print np.angle(1j), np.abs(1+1j), np.sqrt(2)

NumPy fournit des tableaux très performants pour le calcul matriciel :

>>> X, Y = np.zeros(3), np.ones(3)


>>> Z = X + 2*Y
>>> A = np.array([0,1,2])
>>> id3 = np.eye(3)
>>> mat = np.array([[0,1,2],[3,4,5],[6,7,8]])
>>> mat1 = np.zeros([3,3])

Comme pour les listes, on accède à un élément du tableau avec les crochets. Les
indices vont toujours de 0 à n − 1. Pour un tableau à plusieurs dimensions,
on met plusieurs indices dans les crochets : Z[0] est la première ligne de
Z, Z[0,0] est le coefficient (0, 0), première ligne première colonne. On peut
initialiser un tableau avec une liste, comme ici pour A ou en fixant tous les
coefficients à 0 ou à 1. La syntaxe np.zeros((i,j)) est également autorisée,
on se contentera de dire qu’elle est équivalente à np.zeros([i,j]).
On peut définir des tableaux de toute taille et de toute forme, à tout mo-
ment on peut récupérer la taille d’un tableau avec la variable membre shape :

>>> print mat.shape


>>> mat2 = 2*np.ones(mat.shape)
>>> print mat2

Les opérations algébriques usuelles ainsi que les fonctions NumPy peuvent
être appliquée directement sur un tableau et sont effectuées terme à terme,
et ce de manière beaucoup plus rapide qu’en faisant une boucle sur tous les
éléments du tableau. L’opérateur * effectue la multiplication terme à terme. Le
produit scalaire entre deux vecteurs, le produit matrice vecteur et le produit
matriciel se font avec l’opérateur @ La fonction numpy.dot a le même effet pour
les vecteurs et les matrices mais prend un sens différents pour les tableaux de
plus grande dimension.

>>> np.exp(A)
>>> np.cos(mat)
>>> A @ Z
>>> mat @ A
>>> mat @ mat2
>>> id3*mat
>>> id3 @ mat

NumPy possède de nombreux outils pour les nombres aléatoires :

11
>>> np.random.random()
>>> np.random.random(4)
>>> np.random.random((4,4))
Enfin, NumPy propose toutes les options utiles de slicing avec ":" :
>>> mat1[1,:]
>>> mat[1:3,0:2]
Pour une utilisation plus avancée, le module scipy (Scientific Python)
contient toutes les fonctionnalités qu’on attend d’une bibliothèque scienti-
fique : algèbre linéaire, fonctions spéciales, analyse de Fourier... Pour tracer
des courbes, on utilisera le module matplotlib.

9 Le calcul formel avec SymPy


9.1 Pourquoi le calcul formel ?
Comme on l’a vu, le calcul numérique entre floats n’est pas exact. Cela
peut mener à des résultats aberrants :
>>> def f(x):
... y = x**-1
... return [x*y, x*x*y*y, y*y*x*x]
...
>>> f(2)
>>> f(1e-300)
>>> print(np.angle(-1), np.angle(np.conjugate(np.exp(np.pi*1j))))

9.2 Premiers pas sur SymPy


SymPy est un module de calcul formel : il gère les expressions mathéma-
tiques et fait les calculs avec des variables abstraites, donc de manière exacte.
On va appeler tous les éléments du module SymPy de manière à ne pas devoir
rajouter un radical (comme np pour NumPy) à chaque fois.
>>> from sympy import *
On aurait pu faire comme pour NumPy et écrire import sympy as sp. Il
faudrait alors ajouter un préfixe sp. à tous les objets SymPy utilisés. L’im-
portant est de ne pas avoir de conflit de dénomination : les modules SymPy et
NumPy fournissent tous les deux la fonction exp par exemple, mais les deux
fonctions ont des comportements différents. Il faut donc s’assurer qu’on ap-
pelle bien la bonne, et donc qu’elles aient un nom différent. Ici, on appelle la
fonction NumPy avec np.exp et la fonction SymPy avec exp (ou sp.exp si on
a utilisé as sp lors de l’appel de SymPy) : pas de problème.

Avant toute chose, rentrer les deux lignes suivantes pour obtenir des sorties
plus lisibles en LaTeX :

12
>>> from sympy.interactive import printing
>>> printing.init_printing(use_latex=True)

Ce qui suit reproduit en grande partie le tutoriel officiel. Quelques exemples


de base :

>>> a = Rational(1,2)
>>> b = Rational(1,4)
>>> a*2
>>> a + b
>>> pi**2
>>> sin(1)
>>> sin(pi)
>>> pi.evalf()
>>> E.evalf()
>>> (pi + E).evalf(100)

L’infini s’écrit oo :

>>> oo + 1
>>> oo > 1000

Le calcul formel permet de faire du calcul exact : comparons le résultat


obtenu lors du calcul de l’argument de -1 :

>>> np.angle(np.conjugate(np.exp(np.pi*1j))) # calcul numerique


>>> arg(conjugate(exp(pi*I))) # calcul formel

Pour manipuler des expressions algébriques, il va falloir faire appel à nos


propres variables. Les variables symboliques SymPy doivent être décla-
rée explicitement. Elles peuvent ensuite êtres utilisées comme des variables
classiques :

>>> x, y = symbols("x y")


>>> x + y + x - y
>>> (x + y)**2

Le calcul formel permet alors de faire un calcul exact avec des variables
abstraites, puis de substituer ces variables par une valeur numérique dans le
résultat : on utilise pour cela la fonction subs.

>>> x.subs(x,1)
>>> (x + 1 + y + x - y).subs(x,3)
>>> (x + y).subs({x:3, y:2})

On peut donc reprendre la fonction f définie plus haut et calculer f(x), avec
x défini comme symbole SymPy. Comparer les résultats obtenus en calculant
f(1e-300) et en en calculant f(x) puis en substituant x par 1e-300. Expliquer
la différence.

13
>>> f(1e-300)
>>> a = f(x)
>>> print(a[0].subs(x,1e-300), a[1].subs(x,1e-300), a[2].subs(x,1e-300))

Par défaut, SymPy ne présuppose absolument rien sur les variables for-
melles, il les manipule comme des objets abstraits. Cependant, il est souvent
utile de fournir davantage d’information à SymPy : en physique par exemple,
on sait qu’une masse est un réel positif, ce qui permet certaines simplifications.
On le signale lors de la déclaration :

>>> print(sqrt(x**2))
>>> m = symbols("m",positive=True)
>>> sqrt(m**2)

Dans le cas d’équations différentielles typiquement, ces simplifications sont


indispensables pour parvenir à un résultat lisible.

14

Vous aimerez peut-être aussi