Le langage Python a été créé à partir de 1980 par Guido van Rossum et le nom Python fait référence au Monty Python's Flying Circus.
La première version (Python 0.9.0) date de Février 1991 et il a évolué jusqu'à la version 3 (Décembre 2008) pour gagner en maturité. Il est souvent enseigné comme premier langage de programmation, grâce à sa lisibilité et sa simplicité malgré quelques défauts liés à sa syntaxe et sa lenteur d'interprétation.
Python doit son succès :
Voici rapidement quelques spécificités du langage :
L'indentation en Python est primordiale car elle fait apparaître la structure du code mais est également utilisée pour savoir à quel bloc une instruction appartient.
Certain éditeurs remplacent les tabulations par des espaces ce qui peut amener Python à générer des erreurs de syntaxe.
Il est donc conseillé d'utiliser toujours le même environnement de développement (IDE). Parmi les IDE les plus populaires pour Python, on trouve :
On distingue les langages interprétés (PHP, Python, Javascript) des langages compilés (C, C++, Fortran, Pascal).
Pour un langage compilé, un programme appelé compilateur transforme le code en instructions exécutables par le microprocesseur de l'ordinateur.
Pour un langage interprété, un programme appelé interpréteur lit une instruction, l'exécute, puis passe à la suivante.
Le constat est qu'un langage compilé génère du code qui s'exécute beaucoup plus rapidement qu'un langage interprété (cf fractales de Mandelbrot, facteur d'amélioration de 420 / 6 = 70).
On peut, pour mieux appréhender ce concept, utiliser une analogie : imaginons que vous ne parliez que français et que vous soyez amené à consulter un livre en russe. Deux possibilités s'offrent à vous :
En informatique, on distingue le typage dynamique du typage statique.
Le typage consiste à attribuer un type (booléen, entier, chaîne, réel, ...) à une variable afin de savoir comment traiter la variable.
1 + 2 => 3 int + int -> int
"bonjour " + "Jean-Michel" => "bonjour Jean-Michel" (str + str -> str, concaténation)
1 + "bonjour" => ?
TypeError: unsupported operand type(s) for +: 'int' and 'str'
"bonjour" + 1 =?> "bonjour1"
TypeError: can only concatenate str (not "int") to str
"bonjour" + str(1) => "bonjour1"
x = 1 (entier, int)
y = 3.14 (réel, float)
s = "bonjour" (chaine, str)
Notons que le typage dynamique est une notion qui est associée aux langages interprétés.
Par exemple en Python :
>>> x = 10
>>> type(x)
<class 'int'>
>>> id(x) (adresse de x en mémoire)
11753896
>>> x = 3.14
>>> type(x)
<class 'float'>
>>> x = "coucou"
>>> type(x)
<class 'str'>
>>> x = [1,2,3]
>>> type(x)
<class 'list'>
>>> id(x)
123902002645824
Nous allons nous intéresser principalement aux types de données suivants :
bool
) : True
, False
int
) : 1234, -789999range(a,b,c)
) : range(1,10), range(20,1,-1)"str"
) : "bonjour", f"x={x}", b"hello"float
)[ list ]
) ( tuple )
) { dict }
){ set }
)Il existe également d'autres types que nous ne verrons pas dans ce cours : complex, function, frozenset, bytes, bytearray, memoryview, ...
Il suffit généralement de préfixer la valeur avec le nom du type :
# chaine vers entier
entier = int("125")
# entier vers réel
reel = float(123)
# réel vers entier => 3
entier = int(3.14)
# ensemble vers liste
liste = list({1,2,3})
# liste vers ensemble => { 1,2,3 }
ensemble = set([1, 2, 3, 3, 3, 2, 1, 3])
Les booléens sont des valeurs représentant la vérité : True
ou False
.
# Exemple
est_vrai = True
print(type(est_vrai)) # Résultat : <class 'bool'>
Les nombres entiers représentent des valeurs numériques sans partie décimale.
# Exemple
nombre = 42
print(type(nombre)) # Résultat : <class 'int'>
range(b)
représente l'intervalle d'entiers $[0,b[$ de $0$ à $b-1$
range(a,b)
représente l'intervalle d'entiers $[a,b[$, de $a$ à $b-1$
range(a,b,c)
représente l'intervalle d'entiers $[a,b[$ tel que les nombres qu'il représente
sont $a + i × c$ jusqu'à $b-1$
range est utilisé avec les boucles for
.
# Exemple
r = range(10)
print(list(r)) # Résultat : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
r = range(1,10)
print(list(r)) # Résultat : [1, 2, 3, 4, 5, 6, 7, 8, 9], <class 'range'>
r = range(4,1,-1)
print(list(r)) # Résultat : [4,3,2]
Les flottants représentent des nombres réels (cf. flottants).
# Exemple
nombre_decimal = 3.14
epsilon = 8.91e-7
print(type(nombre_decimal)) # Résultat : <class 'float'>
Dans la version 3 de Python, on utilise la représentation IEEE 754 pour les nombres en virgule flottante en double précision :
sys.float_info.max
)sys.float_info.min
)Les chaînes sont utilisées pour représenter du texte.
# Exemple
texte = "Bonjour, Python !"
print(type(texte)) # Résultat : <class 'str'>
Les chaînes sur plusieurs lignes sont introduites par des triples guillemets simples ou doubles :
# Exemple
une_longue_chaine = '''Bonjour, Python !
voici une chaine sur
plusieurs lignes'''
print(type(une_longue_chaine)) # Résultat : <class 'str'>
Un caractère est une chaîne composée d'un seul caractère ou de longueur 1.
On obtient la longueur d'une chaîne grâce à la fonction len()
.
Il existe deux fonctions importantes pour les caractères :
ord("c")
donne la valeur ASCII/UTF-8 du caractèrechr(int)
transforme l'entier en un caractèreord("A")
65
ord("€")
8364
print(chr(65))
A
print(chr(8364))
€
Le package string contient un nombre important de fonctions qui permettent de gérer les chaînes de caractères.
Le type string dispose également de nombreuses fonctions (cf lien) :
split
, strip
, upper
, lower
, swapcase
, startswith
, join
, isalpha
, isdigit
, ...
Enfin, il existe deux autres types de chaînes de caractères :
x = 1
pi = 3.14
print(f"{x} {pi/60} {f(3)}") # Résultat 1 0.052333333333333336 9
Les listes sont des collections ordonnées et modifiables. On peut aussi les qualifier de tableaux car on peut en accéder les éléments par leur indice entier.
Une liste peut contenir des éléments de types différents.
# Exemple
ma_liste = [1, 2, 3, "Python", True]
print(type(ma_liste)) # Résultat : <class 'list'>
print(ma_liste[3]) # Résultat : Python
On obtient la longueur d'une liste grâce à la fonction len()
.
# Exemple
ma_liste = [1, 2, 3, "Python", True]
print(len(ma_liste)) # Résultat 5
On utilise in
pour vérifier la présence de l'élément ou not in
pour
vérifier l'absence de l'élément.
présence
absence
On utilise soit la méthode insert(indice,élément)
, soit la concaténation de listes :
['fiat', 'peugeot', 'renault']
['audi', 'fiat', 'peugeot', 'renault']
On utilise soit la méthode append(élément)
, soit la concaténation de listes :
['peugeot', 'renault', 'toyota']
['peugeot', 'renault', 'toyota', 'volkswagen']
On utilise soit la méthode insert(indice,élément)
, soit la concaténation de listes :
['audi', 'citroën', 'fiat', 'peugeot', 'renault']
['audi', 'fiat', 'hyundai', 'peugeot', 'renault']
On utilise soit l'instruction del
, soit la méthode pop(0)
:
['fiat', 'peugeot', 'renault']
['peugeot', 'renault']
On utilise soit l'instruction del
, soit la méthode pop()
(sans argument) :
['audi', 'fiat', 'peugeot']
['audi', 'fiat']
On utilise soit l'instruction del
, soit la méthode pop(n)
(où $n$ est l'indice de l'élément), soit
la méthode remove(élément)
:
['audi', 'fiat', 'renault', 'volkswagen']
['audi', 'fiat', 'volkswagen']
['audi', 'volkswagen']
La création de listes par compréhension en Python (ou list comprehension) est une syntaxe concise et élégante (qui s'apparente à la syntaxe du langage naturel) permettant de générer des listes à partir d'itérables, tout en appliquant des transformations ou des filtres. Elle remplace souvent une boucle for classique pour rendre le code plus lisible et plus compact.
La syntaxe est la suivante :
[ nouvel_élément for élément in iterable if condition]
La partie if condition
est optionnelle.
Par exemple on peut écrire :
liste = [x for x in range(5)]
print(liste) # [0, 1, 2, 3, 4]
au lieu de :
liste = []
for x in range(5):
liste.append(x)
Voici un exemple avec deux for
:
combinaisons = [(x, y) for x in range(2) for y in range(2)]
print(combinaisons) # [(0, 0), (0, 1), (1, 0), (1, 1)]
Afin de créer une liste d'éléments constants, on peut écrire :
liste_de_zeros = [0 for _ in range(5)]
print(liste_de_zeros) # Résultat : [0, 0, 0, 0, 0]
Voici un autre exemple avec un for
et un if
:
pairs = [x for x in range(10) if x % 2 == 0]
print(pairs) # [0, 2, 4, 6, 8]
Les tuples sont des collections ordonnées mais immuables, ce sont des listes non modifiables.
Elles sont introduites par (...)
.
# Exemple
mon_tuple = (1, 2, 3, "Python")
print(type(mon_tuple)) # Résultat : <class 'tuple'>
print(t[0]) # Résultat : 1
print(t[3]) # Résultat : Python
Les dictionnaires stockent des paires clé-valeur : à une clé est associée une valeur.
Ils sont introduits par {...}
.
# Exemple
mon_dico = {"nom": "Alice", "âge": 30}
print(type(mon_dico)) # Résultat : <class 'dict'>
print(mon_dico["nom"]) # Résultat : "Alice"
.keys()
.values()
.items()
mon_dico = { 1: "rouge", 2:"vert", 3:"bleu", 0:"noir" }
print(mon_dico.values())
# Résultat : dict_values(['rouge', 'vert', 'bleu', 'noir'])
print(mon_dico.keys())
# Résultat : dict_keys([1, 2, 3, 0])
print(mon_dico.items())
# Résultat : dict_items([(1, 'rouge'), (2, 'vert'), (3, 'bleu'), (0, 'noir')])
On utilise in
pour vérifier la présence et not in
pour
vérifier l'absence :
bleu
clé absente
On utilise dict[clé]=valeur
ou la méthode update
:
{3: 'bleu', 0: 'noir', 2: 'vert'}
{3: 'bleu', 0: 'noir', 2: 'vert', 1: 'rouge'}
On utilise del
ou la méthode pop()
qui retourne la
valeur associée à la clé :
{0: 'noir', 2: 'vert', 1: 'rouge'}
rouge
{0: 'noir', 2: 'vert'}
Les ensembles sont des collections non ordonnées de valeurs uniques.
# Exemple
mon_ensemble = {1, 2, 3, 3, 2, 1, 3}
print(mon_ensemble) # Résultat : {1, 2, 3}
print(type(mon_ensemble)) # Résultat : <class 'set'>
print(mon_ensemble[0]) # Résultat : 1
Les éléments d'un ensemble ne sont pas manipulable grâce à l'opérateur []
, il faut transformer l'ensemble en
liste ou alors parcourir les éléments de l'ensemble grâce à une boucle for
.
On utilise in
pour vérifier la présence et not in
pour
vérifier l'absence :
3 est présent
4 n'est pas présent
On utilise :
add(élement)
update({élements})
{{1, 2, 3, 4}
{1, 2, 3, 4, 5, 6}
On utilise :
remove(élement)
discard(élement)
qui ne lève pas d'erreur si l'élément n'est
pas présentpop()
qui supprime un élément quelconque{1, 3, 5, 7, 9, 11, 13, 15}
3 est dans l'ensemble
3 n'est pas dans l'ensemble
1
0 => 5
1 => 7
2 => 9
3 => 11
4 => 13
5 => 15
Les fichier permettent de sauvegarder de l'information sur le disque (HDD/SSD).
On utilise plusieurs notions pour la lecture ou l'écriture des fichiers. notamment :
with open(nom_fichier, mode) as variable
Le paramètre mode
peut prendre plusieurs valeurs ou la combinaison de ces valeurs :
On utilise la fonction
<class '_io.TextIOWrapper'>
On utilise
['Bonjour le monde !\n', 'lorem ipsum dolor\n']
<class '_io.TextIOWrapper'>
Le if
appelée conditionnelle permet de réaliser un traitement si une condition est vraie :
valeurs valides
Le match
(encore appelé switch ou case of dans d'autres langage) et l'équivalent d'un if
mais ne s'applique la plupart du temps qu'à des valeurs scalaires comme les entiers :
a=2
Le while
permet de réaliser le même traitement en boucle tant qu'une condition est vraie :
1
4
Le for
est utilisé pour parcourir containers (list, dict) ou des itérateurs (range) :
0
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
renault
peugeot
fiat
volkswagen
audi
0 renault
1 peugeot
2 fiat
3 volkswagen
4 audi
0 noir
1 bleu
2 rouge
3 vert
0 noir
1 bleu
2 rouge
3 vert
Elle permet l'affichage d'information à l'écran ou l'enregistrement d'information dans un fichier au format texte. Son prototype est le suivant :
print(*args, sep=' ', end='\n', file=None, flush=False)
On affiche un ensemble d'arguments (args) qui sont des entiers, des flottant, decs chaînes, etc.
sep
représente le séparateur entre les arguments, par défaut c'est un espaceend
est le caractère affiché après avoir réalisé print, par défaut c'est le retour
à la ligne mais on peut le remplacer par un caractère vide end=''
pour continuer à
écrire sur la même lignefile
est un fichier si on ne veut pas écrire sur la sortie standardflush
indique si on doit vider le flux d'affichage après printBonjour***le--monde--!
Elle permet de récupérer une chaîne saisie au clavier et terminée par un retour chariot qui ne sera pas pris en compte. Son format est le suivant :
input(prompt='')
saisir un entier:70
<class 'str'> <class 'int'>
70
saisir 3 prénoms: jean-michel karla sarah
jean-michel karla sarah
Un sous-programme est introduit par le mot clé def
suivi du nom du sous-programme et de ses paramètres :
None
Il est nécessaire d'ajouter des commentaires aux sous-programmes afin d'indiquer à d'autres personnes qui voudraient les utiliser, comment ils fonctionnent :
Une fonction peut retourner plusieurs valeurs :
9 5
(7, 3)
3
En Python, il n'existe pas de passage de paramètres par adresse à proprement parler, comme dans des langages comme C ou C++. Cependant, Python utilise un mécanisme appelé passage par objet-référence qui implique que :
Pour simuler un passage par adresse avec des objets immuables, on peut encapsuler la valeur dans un objet mutable comme une liste ou un dictionnaire :
Le typage des paramètres et des valeurs de retour des fonctions a pour objectif :
Cependant, le typage en Python :
Par exemple dans le code suivant, bien qu'on ait spécifié que la fonction
Voici quelques exemples de typage de base ainsi que l'utilisation du package typing :
Une fonction intégrée est une fonction propre au langage et peut parfois être appliquée à différents types :
len()
: renvoie la longueur d'un objet (list, chaîne, dictionnaire)type()
: renvoie le type de l'objetprint()
: affiche un message ou une valeurid()
: renvoie l'identifiant unique d'un objetisinstance()
: vérifie si un objet est une instance d'une classeabs()
: valeur absolueround()
: arrondi au plus prochepow()
: exponentiation (équivalent à **)divmod()
: renvoie le quotient et le reste de la divisionmin(), max()
: valeurs minimale et maximalesum()
: somme des éléments d'un itérableall()
: renvoie True si tous les éléments d'un itérable sont vraisany()
: renvoie True si au moins un élément est vraisorted()
: renvoie une version triée d'un itérablereversed()
: renvoie un itérable inverséenumerate()
: renvoie un itérable avec indicesfilter()
: filtre les éléments d'un itérable.
map()
: applique une fonction à tous les éléments d'un itérablezip()
: regroupe plusieurs itérablesint(), float(), str(), bool()
: conversions de typeslist(), tuple(), set(), dict()
: crée ou convertit des types de collectionsbytes(), bytearray()
: manipulation des données binairescomplex()
: crée un nombre complexeopen()
: ouvre un fichierinput()
: lit une chaîne au clavierexit(), quit()
: quitte le programmeLorsque l'on crée un fichier Python, l'ensemble des variables globales et sous-programmes qu'il définit peuvent être réutilisés dans un autre fichier.
Imaginons que vous avons le fichier suivant :
Afin d'utiliser dans un autre fichier Python, les sous-programmes qu'il définit, trois possibilités s'offrent à nous :
Certains packages nécessitent une version particulière d'un autre package pour pouvoir fonctionner correctement.
On peut connaître la version d'un package :
Soit en ligne de commande :
\$ pip list | grep numpy
numpy 1.26.4
Soit dans l'interpréteur Python ou n exécutant un programme :
\$ python3
Python 3.12.3 (main, Nov 6 2024, 18:32:19) [GCC 13.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy
>>> print(numpy.__version__)
1.26.4
Voici un petit exemple qui montre comment utiliser pythran afin d'améliorer le temps d'exécution d'un programme Python.
Pythran est un compilateur Python vers C++ conçu pour accélérer les calculs numériques et optimiser les performances des programmes Python, en particulier ceux utilisant des tableaux NumPy. Il transforme des fonctions Python annotées en extensions Python compilées en C++, ce qui réduit considérablement le temps d'exécution pour des tâches computationnelles lourdes.
On considère le module suivant qui fournit une fonction
Ainsi que le programme qui utilise cette fonction :
Si on exécute simplement ce programme en Python sur un AMD Ryzen 5 5600G, on obtient :
\$ time python3 utilisation_calcul.py
temps d'exécution= 7.702448606491089
résultat=35401505.8430852
real 0m8,248s
user 0m8,253s
sys 0m0,753s
Soit un temps d'exécution total de $8,253$ secondes dont $7,702$ secondes pour exécuter le code de la fonction
Il suffit de précompiler le code grâce à la commande qui suit, ce qui crée le fichier
\$ pythran calcul.py
\$ ls *.so
calcul.cpython-312-x86_64-linux-gnu.so
Un fichier avec l’extension .so
sous Linux est une bibliothèque partagée (Shared Object).
Il s'agit d’un fichier contenant du code compilé et des données qui peuvent être utilisés par plusieurs
programmes en même temps. Ces fichiers sont comparables aux fichiers .dll
sur Windows.
Ici cela ne fonctionne uniquement car on a ajouté une directive :
pythran export somme_sin_cos(float64[])
devant la fonction à optimiser qui indique quel type de donnée est fourni en entrée à la fonction. Pythran génère alors du code assembleur tout comme le ferait un compilateur C/C++.
Si maintenant on exécute le code comme précédemment, Python va utiliser le fichier objet partagé, mais cela est transparent pour l'utilisateur :
\$ time python3 utilisation_calcul.py
temps d'exécution= 0.9642660617828369
résultat=35404787.98554713
real 0m1,505s
user 0m1,502s
sys 0m0,761s
On met alors moins d'une seconde pour exécuter la fonction compilée.
Dans le fichier .so
, on retrouve le code assembleur qui correspond au calcul :
3d80: movsd xmm0,QWORD PTR [r15] ; charger x ou t[i]
3d85: mov rsi,rbp
3d88: mov rdi,r12
3d8b: add r15,0x8
3d8f: call 2420 < sincos@plt> ; calculer sin(x) et cos(x)
3d94: movsd xmm0,QWORD PTR [rsp+0x18]
3d9a: mulsd xmm0,QWORD PTR [rsp+0x10] ; multiplier sin(x) par cos(x)
3da0: addsd xmm0,QWORD PTR [rsp] ; ajouter au résultat
3da5: movsd QWORD PTR [rsp],xmm0
3daa: cmp rbx,r15 ; continuer tant qu'on est pas
3dad: jne 3d80 <PyInit_calcul@@Base+0xa10> ; à la fin du tableau
3daf: lea rdi,[rsp+0x40]
La fonction sincos appartient normalement à la librairie mathématique et son protoype est
__sincos (double x, double *sinx, double *cosx)
.
Pour résumer :
Sans Pythran | Avec Pythran |
7,70s | 0,96s |
On notera cependant qu'on obtient pas le même résultat car les valeurs ne sont pas les mêmes dans les deux cas.