Cours Python
Cours Python
1 Chapitre 1 - Introduction 3
6 Chapitre 6 - Listes 31
8 Chapitre 8 - Dictionnaires 43
9 Chapitre 9 - Tuples 49
13 Chapitre 13 - Bibliothèques 69
16 Chapitre 16 - Héritage 93
17 Chapitre 17 - Décorateurs 99
i
ii
Programmation en Python
Ce cours est destiné à toute personne qui voudrait découvrir la programmation en général et le langage Python en
particulier - il est également disponible en pdf à ladresse : [Link]
Le pdf comme le site sont régulièrement mis à jour, et les sources sont disponibles publiquement sur sourcehut.
Jespère quil vous plaira ! Sinon, nhésitez pas à vous rendre sur ma page de contact pour me faire part de vos remarques.
Enfin, notez que ce cours est placé sous licence CC BY 4.0, ce qui signifie que vous pouvez le partager et le modifier
comme bon vous semble, à la seule condition de me créditer de façon appropriée.
Bonne lecture !
Chapitre 1 - Introduction
— Excellent langage pour débuter - il a dailleurs été créé avec cet objectif en tête.
— Mon langage préféré.
— Facile à installer quelque soit le système dexploitation (Linux, macOS, Windows).
Cest une interface universelle. Pour faire des programmes en lignes de commande, on a juste besoin de manipuler du
texte, et cest le plus simple au début.
Il est important de noter que les chapitres sont prévus pour être lus dans lordre.
Par exemple, pour bien comprendre le chapitre 10 sur les classes, il faut avoir bien compris les chapitres précédents,
car les explications du chapitre 10 supposent que les notions introduises aux chapitres 1 à 9 sont acquises !
Il est en conséquence recommandé darriver à résoudre les exercices en fin de chapitre avant de passer au suivant.
3
Programmation en Python
1.2.3 Historique
1.2.5 Python3
— Très puissant.
— Ancien, mais toujours dactualité.
— Indispensable dans de nombreux cas.
— Écrire des programmes qui fonctionnent dans la ligne de commande est (relativement) simple.
— Possibilités infines, même si on ne fait que manipuler du texte.
Notez le petit bout de texte avant le curseur : on appelle ça une invite de commande (prompt en anglais).
Pour lancer une commande, on tape son nom, suivi parfois dun certain nombre de mots séparés par des espaces, puis
on appuie sur entrée.
Par exemple, on peut utiliser ls (ou dir sous Windows) pour lister le contenu du répertoire courant :
5
Programmation en Python
Et on peut utiliser cd suivi du nom dun répertoire pour changer de répertoire courant :
2.2 Installation
Linux
Il y a toutes les chances que Python soit déjà installé sur votre distribution. Pour vous en assurer, tapez :
python3 --version
Sinon, installez le paquet correspondant à Python3 (quelque chose comme sudo apt install python3)
macOS
Windows
Je vous conseille pour commencer dutiliser un éditeur de texte basique. Vous navez pas besoin dun IDE, surtout si vous
débutez
— Linux ; gedit, kate, ;
— macOS : CotEditor ;
— Windows : Notepad++.
Jinsiste sur simple. Inutile dinstaller un IDE pour le moment.
2.3.1 Définition
print("Bonjour, monde")
# affiche: Bonjour, monde
cd Documents/python/
python3 [Link]
Bonjour, monde
Vous savez maintenant comment exécuter du code Python dans nimporte quel fichier :
2.3.4 print()
Revenons sur ce quil sest passé : nous avions le mot print avec des parenthèses et quelque chose à lintérieur des
parenthèses, et ceci a provoqué laffichage du contenu des parenthèses dans le terminal.
2.3.5 Commentaires
La deuxième ligne, quant à elle, a été complètement ignorée par linterpréteur parce quelle commençait par un #. Il
sagit dun commentaire, et il sert principalement aux humains qui lisent le code.
La plupart des exemples de ce cours contiendront un ou plusieurs appels à print afin dafficher les opérations que
linterpréteur a effectué.
Pour lire ce cours de manière efficace, il est conseillé de lancer les exemples de code sur votre machine, et de vérifier
si ce qui est affiché sur votre machine correspond à ce qui est écrit dans le cours.
Il est aussi recommandé de ne pas copier/coller le code.
À la place, prenez le temps de retaper le code dans votre éditeur.
Plusieurs raisons à cela :
— Recopier le code vous aidera à vous souvenir de la syntaxe
— Si vous faites des erreurs, Python vous préviendra et vous découvrirez les erreurs courantes
— Il est possible que des erreurs subsistent dans ce cours, et procéder ainsi nous permettra de les corriger.
print(1 + 2)
# affiche: 3
print(6 - 3)
# affiche: 3
print(0.5 + 0.2)
# affiche: 0.7
print(10 / 2)
# affiche: 5.0 (et non '5')
Notez que les flottants sont imprécis, ce qui explique le 5 à la fin de laffichage de la division de 10 par 3 :
print(10 / 3)
# affiche:3.3333333333333335
print(14 // 3)
# affiche: 4
print(14 % 3)
# affiche: 2
print(1 + 2 * 3)
# affiche: 7
print((1 + 2) * 3)
# affiche: 9
3.1.1 Instructions
Pour linstant, dans tous les exemples de code, chaque ligne quon a écrit contenait une instruction.
Un instruction a un effet sur le programme dans lequel elle est présente.
Par exemple, linstruction print("bonjour") affiche ńăbonjourăż dans le terminal. On dit que linstruction est exécu-
tée.
En règle générale, les instructions sont exécutées une par une, de haut en bas.
3.1.2 Expressions
1 + (2 * 3)
11
Programmation en Python
À droite du plus, on a une expression 2 + 3. Quand Python évaluera lexpression, il verra dabord le littéral 1 et le +,
puis il évaluera lexpression à droite (2 * 3 = 6), et finalement lexpression en entier (1 + 6 = 7).
Notez que si vous écrivez une ligne contenant une expression, elle est évaluée mais rien nest exécuté :
print("Bonjour")
40 + 2
print("Au revoir")
# Affiche: 'Bonjour' puis 'Au revoir'
Ici lexpression 40 + 2 a été évaluée, mais Python na rien fait avec le résulat, il est simplement passé à linstruction
suivante.
On peut associer des variables à des valeurs en les plaçant de part et dautre du signe = : on appelle cette opération une
assignation :
a = 2
a = 2
print(a)
# affiche: 2
En fait, on peut assigner nimporte quelle expression à une variable, et pas simplement des littéraux :
a = 1 + 2
print(a)
# affiche: 3
a = 1
print(a + 2) # ici "a" a été remplacé par 1
# affiche: 3
a = 1
print(a + 2)
# affiche: 3
print(a)
# affiche: 1
Autres exemples :
x = 1
y = 2
print(x+y) # ici x a été remplacé par 1 et y par 2
# affiche: 3
On peut aussi changer la valeur dune variable en assignant une nouvelle valeur à celle-ci :
a = 2
print(a)
a = 3
print(a)
# affiche: 2, puis 3
La notation += permet de combiner addition et assignation : les deux exemples ci-dessous sont équivalents :
x = 3
x = x + 1
x = 3
x += 1
Ci-dessus jai utilisé des noms de variables à une lettre, mais il est préférable davoir des noms longs et descriptifs.
Aussi, la convention est de :
— les écrire en minuscules ;
— de séparer les mots par des tirets bas (underscore) :
score = 42
âge_moyen = 22
On dit quon utilise le snake case (parce que ça ressemble vaguement à un serpent)
Notez que certains mots ne peuvent pas être utilisés comme nom de variables. On les appelle des mots-clés. La liste est
disponible ici : [Link]
Les chaînes de caractères, aussi appelées ńăstringsăż, permettent de représenter du texte. On a utilisé une string pour
afficher ńăbonjour mondeăż dans le chapitre précédent.
On écrit toujours les strings entre guillemets (ou quotes).
soit avec des doubles guillemets :
print("Bonjour monde!")
# affiche: Bonjour monde!
print('Bonjour monde!')
# affiche: Bonjour monde!
On peut mettre des simples quotes dans des double quotes et vice-versa :
3.2.2 Échappement
On peut aussi échapper des caractères avec la barre oblique inversée \\ - backslash.
3.2.3 Concaténation
On peut construire de longues chaînes de caractères en en concaténatant de plus petites, avec lopérateur + :
name = "John"
message = "Bonjour " + name + " !"
print(message)
# affiche: Bonjour John !
3.2.4 Répétition
On peut construire une longue string en répétant la même petite string plusieurs fois avec lopérateur * :
message = "Na" * 3
print(message)
# affiche: NaNaNa
3.3 Types
On a vu quon pouvait utiliser + à la fois pour additionner des nombres ou concaténer des strings. Mais on ne peut pas
utiliser + avec une string dun côté et un entier de lautre :
a = 42
b = 4
c = a + b # ok
résultat = a + prénom
# affiche:
# TypeError: can only concatenate str (not "int") to str
Ceci est notre premier message derreur : si linterpréteur est incapable dexécuter une instruction, il affiche un message
derreur et sinterrompt immédiatement.
3.3.1 Conversions
On peut convertir un entier en string en utilisant le mot str et des parenthèses autour de lexpression :
x = 40
y = 2
message = "La réponse est: " + str(x + y)
print(message)
# affiche: La réponse est 42
3.3. Types 15
Programmation en Python
Inversement, on peut convertir un string en entier en utilisant le mot int et des parenthèses :
quarante_en_chiffres = "40"
réponse = int(quarante_en_chiffres) + 2
print(réponse)
# affiche: 42
taille_sur_le_passeport = "1.62"
taille_en_mètres = float(taille_sur_le_passeport)
3.4 Booléens
On appelle booléenne une valeur qui peut être soit vraie, soit fausse.
En Python, les littéraux True et False représentent respectivement les valeurs vraies et fausses.
(Notez quils commencent par une majuscule)
3.4.1 Comparaisons
Certaines expressions renvoient des booléens, cest à dire soit la valeur True, soit la valeur False
== égal
!= différent
> strictement supérieur
>= supérieur ou égal
< strictement inférieur
<= inférieur ou égal
Par exemple :
a = 2
b = 3
print(a > b)
# affiche: False
print(2 + 2 == 4)
# affiche: True
Notez que deux valeurs avec des types différents sont en général considérés comme différents :
1 == "1" # False
Cette règle admet des exceptions, par exemple un flottant peut être égal à un entier :
1 == 1.0 # True
not négation
and et
or ou
Exemples :
a = not True
print(a)
# affiche `False`
il_pleut = True
j_ai_un_parapluie = False
print(il_pleut and j_ai_un_parapluie)
# affiche: False
3.4. Booléens 17
Programmation en Python
Pour linstant, toutes les instructions que nous avons écrites ont été exécutée une par une et dans lordre dapparition dans
le code source.
De plus, chaque ligne était constituée dune unique expression.
Modifier lordre dexécution de ces instructions sappelle le contrôle de flux, et cest lessence même de la programmation !
4.1.1 if
On peut utiliser le mot-clé if pour autoriser ou empêcher lexécution des instructions suivantes :
a = 3
b = 4
if a == b:
print("a et b sont égaux")
# n'affiche rien
if a = 3:
print("a égale 3")
# affiche: SyntaxError
19
Programmation en Python
a = 3
b = 4
if a == b:
# début du bloc
print("a et b sont égaux")
c = 2 * a
# fin du bloc
Notez quon reprend lordre habituel dexécution des instructions sil y a un bloc indenté dans lautre sens après le if :
a = 3
b = 4
if a == b:
# début du bloc
print("a et b sont égaux")
c = 2 * a
# fin du bloc
# on est après le bloc if
print("on continue")
Ce code naffiche pas ńăa et b sont égauxăż, mais affiche bien ńăon continueăż.
if / else
On peut utiliser le mot-clé else après un condition en if pour exécuter un bloc si la condition est fausse :
a = 3
b = 4
if a == b:
print("a et b sont égaux")
else:
print("a et b sont différent")
# affiche: a et b sont différents
if / elif
age = 23
if age < 10:
print("âge inférieur à dix")
elif 10 <= age < 20:
print("âge entre 10 et 20")
elif 20 <= age < 40:
print("âge entre 20 et 40")
else:
print("âge supérieur à 40")
# affiche: âge entre 20 et 40
while
On peut utiliser le mot-clé while pour répéter un bloc tant quune condition est vraie :
i = 0
while i < 3:
print(i)
i = i + 1
0
1
2
Boucle infinie
while True:
print("spam!")
i = 0
while True:
i = i + 1
print(i)
if i > 3:
break
1
2
3
4
Jusquici les expressions quon a utilisé donnait un booléen une fois évaluées, mais une expression après un if peut être
dun autre type.
Par exemple, un entier :
x = 0
if x:
print("x n'est pas nul")
else:
print("x est nul")
On dit que 0 est Falsy, parce quaprès un if, il se comporte comme une expression qui vaudrait False.
Réciproquement, tous les entiers sauf 0 sont Truthy, parce quils se comportent comme une expression qui vaudrait
True :
y = 6
if y:
print("y n'est pas nul")
else:
print("y est nul")
message = ""
if message:
print("le message n'est pas vide")
else:
print("le message est vide")
# affiche: le message est vide
En fait, on peut utiliser tous les opérateurs booléens avec des expressions quelconques :
message = ""
if not message:
print("le message est vide")
# affiche: le message est vide
score = 42
message = "pas un message vide"
if message and score:
print("le message et le score sont truthy")
# affiche: le message et le score sont truthy
4.3 Exercice
4.3.1 Consignes
4.3.2 Indices
Pour bloquer le programme et lire une entrée utilisateur, vous pouvez utiliser la ligne suivante :
entrée_utilisateur = input()
Cette instruction va :
— interrompre le script ;
— lire ce que lutilisateur tape jusquà ce quil tape ńăentréeăż ;
— et assigner la valeur correspondante à la variable entrée_utilisateur.
Pour tirer un nombre au hasard entre 1 et 100, vous pouvez utiliser les deux lignes suivantes :
import random
nombre_secret = [Link](0, 100)
À la fin de ces deux instructions, une valeur entre 1 et 100 tirée au hasard sera assignée à la variable nombre_secret.
4.3. Exercice 23
Programmation en Python
4.3.3 Squelette
Pour commencer, copier le code suivant dans un fichier (par exemple, [Link])
import random
while True:
if entrée_utilisateur == nombre_secret:
print("bravo")
break
else:
print("mauvaise réponse")
entrée_utilisateur = int(input())
Si vous lancez python3 [Link] vous constaterez que le jeu est très difficile parce que le programme affiche
simplement ńămauvaise réponseăż en boucle jusquà ce que lutilisateur devine le nombre secret.
Votre but est de modifier le code pour implémenter correctement le programme, et ainsi rendre le jeu jouable :)
Si vous lisez ceci en dehors dun cours et que vous êtes coincés, nhésitez pas à me contacter.
Bon courage !
5.1 Fonctions
Définition :
def dire_bonjour():
print("Bonjour")
Notez quon utilise aussi le snake case pour les noms de fonctions, comme pour les noms de variables.
— avec le mot-clé def ;
— avec un : à la fin et un bloc indenté (appelé le corps de la fonction).
Appel :
dire_bonjour()
def dire_bonjour():
print("Bonjour")
dire_bonjour()
# Affiche: bonjour'
25
Programmation en Python
Ici on vient dajouter une nouvelle fonctionnalité à Python. Avant quon définisse la fonction dire_bonjour(), il ne savait
pas dire bonjour, il savait uniquement afficher des messages à lécran.
On dit quon a créé une abstraction. Et cest une technique extrêmement utile en programmation.
def dire_bonjour(prénom):
print("Bonjour " + prénom)
dire_bonjour("Germaine")
# Ceci:
dire_bonjour("Dimitri")
# Lui-même équivalent à:
print("Bonjour " + "Dimitri")
Exemple complet :
def dire_bonjour(prénom):
print("Bonjour " + prénom)
dire_bonjour("Germaine")
# affiche: Bonjour Germaine
prénom_de_charlotte = "Charlotte"
dire_bonjour(prénom_de_charlotte)
# affiche: Bonjour Charlotte
def dire_bonjour(prénom):
print("Bonjour " + prénom)
dire_bonjour("Dimitri") # Ok
print(prénom) # Erreur
Les variables définies en dehors du corps des fonctions sont disponibles partout :
def dire_bonjour(prénom):
print(salutation + prénom)
dire_bonjour("Dimitri")
Une variable peut avoir en ńăcacherăż une autre si elle a une portée différente :
def dire_bonjour():
prénom = Max # portée: uniquement dans
# le corps dire_bonjour
On peut également appeler une fonction dans une expression à droite dune assignation de variable.
Dans ce cas, la valeur de lexpression est obtenue en exécutant le corps de la fonction jusquà rencontrer linstruction
return et en évaluant lexpression à droite du return.
Par exemple :
def retourne_42():
return 42
x = retourne_42()
print(x)
# Affiche: 42
def peut_conduire(âge):
if âge < 18:
return False
else:
return True
x = peut_conduire(16)
print(x)
# Affiche: False
On peut mettre autant darguments quon veut, séparés par des virgules :
résultat = soustraction(5, 4)
print(résultat)
# affiche: 1
En Python, on peut aussi utiliser le nom des arguments au lieu de leur position :
def dire_bonjour(prénom):
print("Bonjour " + prénom)
dire_bonjour(prénom="Gertrude")
# Affiche: Bonjour Gertrude
Appel :
dire_bonjour("Thomas")
# affiche: Bonjour Thomas
dire_bonjour("Thomas", enthousiaste=True)
# affiche: Bonjour Thomas!
dire_bonjour("Thomas", enthousiaste=False)
# affiche: Bonjour Thomas
Fonctions qui sont toujours présentes dans linterpréteur. On en a déjà vu quelques unes :
— print(), input() : écrire et lire sur la ligne de commande ;
— str(), int() : convertir des entiers en strings et vice-versa.
Il y en a tout un tas !
La liste ici : [Link]
prénom = "Charlotte"
print("Bonjour", penom)
print("Ça va ?")
Bonjour Charlotte
Ça va ?
a = "chauve"
b = "souris"
print(a, b, sep="-")
chauve-souris
Ou de changer le caractère de fin :
5.7 Exercice
Le but de lexercice est dafficher un sapin de largeur arbitraire dans la console, comme ceci :
#
###
#####
#######
#########
#
#
Le sapin est composé dune suite de lignes, chacune des lignes étant constituée uniquement de dièses.
Il y a deux parties au sapin : les feuilles qui forment un triangle de largeur 1 tout en haut jusquà une ligne de largeur 9
tout en bas, et un pied constitué de deux dièses superposés
5.7.1 Indices
Pour construire une chaîne de caractères constituée uniquement de dièses vous pouvez utiliser lexpression suivante :
cinq_diéses = "#" * 5
print(cinq_diéses)
#####
5.7.2 Consignes
largeur = 9
def affiche_feuilles():
affiche_ligne(5, 6)
affiche_ligne(4, 7)
# à compléter
def affiche_pied():
affiche_ligne(5, 6)
# à compléter
def affiche_sapin():
affiche_feuilles()
affiche_pied()
affiche_sapin()
Chapitre 6 - Listes
6.1.1 Définition
liste_vide = []
trois_entiers = [1, 2, 3]
31
Programmation en Python
liste_vide = []
taille = len(liste_vide)
print(taille)
# affiche: 0
trois_entiers = [1, 2, 3]
taille = len(trois_entiers)
print(taille)
# affiche: 3
Avec + :
print(prénoms)
# affiche: ['Alice', 'Bob', "Charlie", 'Eve']
scores = [1, 2, 3]
scores = scores + 4
# erreur
scores = [1, 2, 3]
scores = scores + [4]
print(scores)
# affiche: [1,2,3,4]
On peut aussi utiliser += pour combiner lassignation et la concaténation, comme avec les nombres :
a = 3
a += 2
print(a)
# Affiche: 5
x = [1, 2]
x += [3, 4, 5]
print(x)
# Affiche: [1, 2, 3, 4, 5]
On peut récupérer un élément dune liste à partir de son index, en utilisant [i] où i est lindex de lélément.
Les index valides vont de 0 à n-1 où n est la taille de la liste :
print(fruits[0])
# affiche: "pomme"
print(fruits[1])
# affiche: "orange"
print(fruits[2])
# affiche: "poire"
fruits[3]
# erreur: IndexError
6.2 Itération
Bonjour Alice
Bonjour Bob
Bonjour Charlie
6.2. Itération 33
Programmation en Python
6.2.2 break
Comme pour les boucles while, on peut interrompre la boucle for avec break :
Bonjour Alice
Bonjour Bob
6.2.3 continue
On peut interrompre lexécution du bloc courant (et uniquement le bloc courant) avec le mot-clé continue :
Bonjour Alice
Bonjour Charlie
Si on met une liste vide, if se comportera comme si on avait mis une valeur fausse, et si la liste nest pas vide , if se
comportera comme si on avait mis une valeur vraie :
ma_liste = [1, 2, 3]
if ma_liste:
print("ma_liste est truthy")
else:
print("ma_liste est falsy")
# affiche: ma_liste est truthy
mon_autre_liste = []
if mon_autre_liste:
print("mon_autre_liste n'est pas vide")
else:
print("mon_autre_liste est vide")
# affiche: mon_autre_liste est vide
On dit que les listes vides sont Falsy et les listes non-vides Truthy.
On peut utiliser lopérateur == avec deux listes de part et dautre. Les listes seront considérées comme égales si :
— elles ont la même taille ;
— tous leurs éléments sont égaux un à un en respectant lordre :
x = [1]
y = [1, 2]
print(x == y)
# affiche: False, x et y n'ont pas la même taille
x = [1, 2]
y = [1, 3]
print(x == y)
# affiche: False, x et y n'ont pas les mêmes éléments
x = [1, 2]
y = [2, 1]
print(x == y)
# affiche: False, x et y ont les mêmes éléments, mais
# pas dans le bon ordre
x = [1, 2]
y = [1]
y += [2]
print(x == y)
# affiche: True, x et y ont les mêmes éléments, dans le même ordre
for c in "vache":
print(c)
v
a
c
h
e
print("e" in "vache")
# affiche: True
print("x" in "vache")
# affiche: False
Notez quon peut aussi utiliser in pour tester si une chaîne de caractères est contenue dans une autre :
print("ch" in "vache")
# affiche: True
prénom = "Charlotte"
l = prénom[0]
print(l)
# affiche: "C"
prénom = "Charlotte"
prénom[0] = "X"
# erreur: TypeError
6.5 Exercice
______
entrer une lettre
e
_____e
entrer une lettre
a
_a___e
entrer une lettre
u
_au__e
entrer une lettre
q
_au__e
entrer une lettre
(suite sur la page suivante)
6.5.1 Consignes
6.5. Exercice 37
Programmation en Python
7.1 None
7.1.1 Définition
En réalité, toutes les fonctions python retournent quelque chose, même quand elles ne contiennent pas le mot-clé
return.
def ne_renvoie_rien():
x = 2
resultat = ne_renvoie_rien()
print(resultat)
# affiche: None
39
Programmation en Python
La plupart des fonctions que nous avons vu échouent si on leur passe None en argument :
x = len(None)
# erreur: TypeError
x = None < 3
# erreur: TypeError
x = int(None)
# erreur: TypeError
x = str(None)
print(x)
# affiche: 'None'
x = ne_renvoie_rien()
print(x is None)
# affiche: True
7.2 pass
À cause de lorganisation du flot de contrôle en blocs indentés, on ne peut pas vraiment avoir de blocs vides. Mais
parfois, on a besoin dun bloc qui ne fasse rien - cest là que le mot-clé pass rentre en jeu.
pass représente une instruction qui ne fait rien.
Un exemple :
une_condition = False
if une_condition:
pass
else:
print("une_condition n'est pas vraie")
On peut aussi - et cest lusage le plus courant - utiliser pass pour définir une fonction qui ne fait rien :
def ne_fait_rien():
pass
7.2. pass 41
Programmation en Python
Chapitre 8 - Dictionnaires
8.1.1 Définition
dictionnaire_vide = {}
Ensuite, on utilise : pour séparer la clé et la valeur. Enfin, les paires de clé/valeur sont séparées par des virgules , :
43
Programmation en Python
Avec [], comme pour les listes, mais avec une clé à la place dun index :
print(scores["john"])
# affiche: 10
print(scores["bob"])
# affiche: 42
print(scores["charlie"])
# erreur: KeyError
rappel : Ceci ne fonctionne pas avec les listes. On ne peut pas ńăcréerăż de nouveaux éléments dans la liste juste avec
un index :
ma_liste[3] = "d"
# erreur: IndexError
8.1.6 del
mon_entier = 42
mon_entier += 3
print(mon_entier)
# affiche: 45
del mon_entier
mon_entier += 1
# erreur: NameError
john 10
bob 42
Les dictionnaires vides sont falsy, et tous les autres dictionnaires sont truthy :
mon_autre_dico = {}
if mon_autre_dico:
print("mon_autre_dico n'est pas vide")
else:
print("mon_autre_dico est vide")
# affiche: mon_autre_dico est vide
On peut vérifier si une clé est présente dans un dictionnaire avec le mot clé in, un peu comme pour les listes :
print("charlie" in scores)
# affiche: False
Deux dictionnaires sont considérés égaux sils ont les mêmes clés et les mêmes valeurs. Lordre na pas dimportance :
8.4 Exercice
8.4.1 Consignes
python [Link] 1 km mi
Il y a trois ńămotsăż après le nom du fichier, séparés par des espaces. On appelle ces mots les ńăargumentsăż du
programme.
Ici, 1 est la valeur de départ, km labréviation de lunité de départ et mi labréviation de lunité darrivée.
Voici un tableau pour vous aider :
8.4.2 Squelette
def trouver_coefficient(unité):
return 1.0
def main():
nombre_arguments = len([Link]) - 1
if nombre_arguments != 3:
print("3 arguments attendus, mais", nombre_arguments, "reçu(s)")
return
valeur = float([Link][1])
unité_de_départ = [Link][2]
unité_d_arrivée = [Link][3]
valeur_de_sortie = convertir(valeur, unité_de_départ, unité_d_arrivée)
print("{:.6}".format(valeur_de_sortie))
(suite sur la page suivante)
8.4. Exercice 47
Programmation en Python
main()
Vous noterez que le programme est capable de récupérer la valeur, lunité de départ et lunité darrivée correctement,
mais que le résultat est toujours égal à 1.0.
Votre objectif est de finir limplémentation pour réaliser effectivement la conversion.
À noter : vous navez pas besoin de changer limplémentation des fonctions main() et convertir(), et ne vous in-
quiétez pas si vous ne comprenez pas exactement ce que fait la fonction main(), on expliquera en détail comment
elle fonctionne dans un chapitre ultérieur. Pour terminer lexercice, vous navez besoin que de modifier les fonctions
trouver_coefficient(), conversion_en_mètres() et conversion_depuis_mètres().
À vous de jouer !
Chapitre 9 - Tuples
9.1 Définition
Un tuple est un ensemble ordonné et immuable déléments (il est aussi appelé n-uplet dans la documentation Python).
Le nombre, lordre et la valeur des éléments sont fixes.
tuple_vide = ()
tuple_à_un_élement = (1,) # notez la virgule
tuple_à_deux_éléments = (1, 2) # on dit aussi: "couple"
Comme les listes, les tuples peuvent contenir des éléments de types différents :
49
Programmation en Python
9.4 Accès
print(mon_tuple[1])
# affiche: "bonjour"
9.5 Modification
Cest interdit :
Avec in :
print(14 in mon_tuple)
# affiche: True
print(13 in mon_tuple)
# affiche: False
9.7 Dissociation
print(side_kick)
# affiche: Robin
(héros,) = couple
# erreur: ValueError (1 != 2)
# Gare à la virgule:
héros, = couple
# erreur: ValueError (1 != 2)
9.9 Pièges
print(premier)
# affiche: pomme
print(deuxième)
# affiche: banane
print(troisième)
# affiche: orange
# Avant:
if (
ma_valeur == "nord" or
ma_valeur == "sud" or
ma_valeur == "ouest" or
ma_valeur == "est"):
print("direction", ma_valeur)
# Après:
if ma_valeur in ("nord", "sud", "est", "ouest"):
print("direction", ma_valeur)
def tire_carte():
valeur = "10"
couleur = "trèfle"
return (valeur, couleur)
v, c = tire_carte()
print(v, "de", c)
# affiche: 10 de trèfle
Ce quon a vu jusquici :
— des types simples (entiers, booléens, ) ;
— des structures de données (listes, dictionnaires, ) ;
— des fonctions qui manipulent ces types ou ces structures de données ;
— ds fonctions qui sappellent les unes les autres.
On appelle cet ensemble de concepts, cette façon décrire du code, un paradigme, et cest un paradigme procédural.
On va passer à un autre paradigme : lorienté objet.
Une meilleure définition, cest de dire que la programmation orientée objet permet de mettre au même endroit :
— des données ;
— des fonctions qui opèrent sur ces données.
Limportant cest que les deux aillent ensemble !
Note : ce nest pas la meilleure définition de lorienté objet, mais on sen contentera pour le moment
53
Programmation en Python
class MaClasse:
# du code ici
Notez quon nutilise pas le snake case pour les noms de classes, mais le Pascal Case (ou Camel Case) : Le nom
commence par une majuscule, et on alterne minuscules et majuscules pour séparer les mots.
Comme les fonctions, les classes contiennent un corps, qui est le bloc indenté en dessous du mot-clé class, de nom
de la classe et du : en fin de ligne.
Les classes sont utilisées pour construire des instances.
class MaClasse:
pass
Dans ce cas, on crée une instance en mettant le nom de la classe suivi dune paire de parenthèses, un peu comme pour
appeler une fonction :
mon_instance = MaClasse()
Ici, mon_instance est une instance de la classe MaClasse. Notez que mon_instance utilise snake case, comme
toutes les variables quon a vues jusquici.
10.6 Attributs
y = a.x
func = a.x
func(10)
Ici, on crée une variable func qui prend la valeur de lattribut x dans linstance a, puis on lappelle avec largument 10 à
la ligne suivante.
Le code suivant fait exactement la même chose, mais avec une ligne de moins :
a.x(10)
On peut créer des attributs dans nimporte quel instance, en utilisant lassignation :
mon_instance = MaClasse()
class MaClasse:
def ma_méthode(self):
return 42
Notez que les méthodes sont aussi des attributs. Leur valeur est une fonction qui se comporte légèrement différemment
des fonctions quon a vu jusquici.
Une méthode ne peut être appelée que depuis une instance de la classe :
class MaClasse:
def ma_méthode(self):
return 42
ma_méthode()
# erreur: NameError
mon_instance = MaClasse()
résultat = mon_instance.ma_méthode()
print(résultat)
# affiche: 42
Notez quon ne passe pas dargument quand on appelle ma_méthode depuis linstance.
class MaClasse:
def affiche_attribut_x(self):
# Accès à l'attribut `x` dans `self`
print(self.x)
mon_instance = MaClasse()
mon_instance.x = 42
mon_instance.affiche_attribut_x()
# affiche: 42
class MaClasse:
def crée_attribut_x(self):
self.x = 42
def affiche_attribut_x(self):
print(self.x)
mon_instance = MaClasse()
mon_instance.affiche_attribut_x()
# erreur: `mon_instance` n'a pas d'attribut `x`
mon_instance.crée_attribut_x()
mon_instance.affiche_attribut_x()
# affiche: 42
Les méthodes peuvent aussi prendre plusieurs arguments, en plus de self, mais self doit toujours être le premier
argument.
Par example, pour créer un attribut avec une certaine valeur :
class MaClasse
def crée_attribut_x(self, valeur_de_x):
self.x = valeur_de_x
def affiche_attribut_x(self);
print(self.x)
mon_instance = MaClasse()
mon_instance.crée_attribut_x(42)
mon_instance.affiche_attribut_x()
# affiche: 42
Comme les méthodes sont aussi des attributs, les méthodes dune instance peuvent sappeler les unes les autres :
class MaClasse:
def méthode_1(self):
print("démarrage de la méthode 1")
print("la méthode 1 affiche bonjour")
print("bonjour")
print("fin de la méthode 1")
def méthode_2(self):
print("la méthode 2 appelle la méthode 1")
self.méthode_1()
print("fin de la méthode 2")
mon_instance = MaClasse()
mon_instance.méthode_2()
Si vous définissez une méthode nommée __init__, celle-ci est appelée automatiquement quand linstance est
construite.
On dit que cest une méthode ńămagiqueăż parce quelle fait quelque chose sans quon lappelle explicitement.
On utilise souvent __init__ pour créer des attributs :
class MaClasse:
def __init__(self):
self.x = 1
self.y = 2
mon_instance = MaClasse()
On prend souvent les valeurs des attributs à créer en arguments de la méthode __init__ :
class MaClasse:
def __init__(self, x, y):
self.x = x
self.y = y
Dans ce cas, les arguments de la méthode __init__ apparaissent à lintérieur des parenthèses après le nom de la classe :
mon_instance = MaClasse(3, 4)
print(mon_instance.x)
# affiche: 3
print(mon_instance.y)
# affiche: 4
Note : Pour cette raison, __init__ est souvent appelé le constructeur de la classe.
Une bonne pratique consiste à créer tous les attributs nécessaires dans le constructeur.
Si les atttributs ont une valeur par défaut, ou une valeur initiale, on peut les créer dans le constructeur directement.
Sinon, on créera lattribut avec une valeur qui viendra dun paramètre du constructeur (souvent avec le même nom)
Premier example :
class Point2D:
# x et y n'ont pas de bonne valeur par défault,
# donc on utilise un constructeur qui prend
# x et y en paramètre
def __init__(self, x, y):
self.x = x
self.y = y
origine = Point2D(0, 0)
Deuxième example :
class Joueur:
# Quand on crée un joueur, il a un score à 0
def __init__(self, nom):
[Link] = nom
[Link] = 0
alice = Joueur("alice")
10.13 Récapitulatif
Ainsi, on peut ranger au même endroit des données et des fonctions opérant sur ces données.
Les données sont les attributs, et les fonctions opérant sur ces attributs sont les méthodes.
On peut ainsi séparer les responsabilités à lintérieur dun code en les répartissant entre plusieurs classes.
10.13. Récapitulatif 59
Programmation en Python
# Dans [Link]
a = 42
Comme un fichier = un module, on vient de créé un module bonjour contenant une variable a.
Si maintenant on crée un fichier [Link] dans le même répertoire, on peut accéder à cette variable en impor-
tant le module avec le mot-clé import :
# Dans [Link]
import bonjour
print(bonjour.a)
# affiche: 42
Note : Le nom du module est écrit directement, ce nest pas une chaîne de caractères.
On voit que la variable a dans [Link] est devenue un attribut du module bonjour lorsquil a été importé.
61
Programmation en Python
# Dans [Link]
a = 42
def dire_bonjour():
print("Bonjour!")
# Dans [Link]
import bonjour
bonjour.dire_bonjour()
# affiche: Bonjour!
# Dans [Link]
print("Je suis le module bonjour et tu viens de mimporter")
def dire_bonjour():
....
# Dans [Link]
import bonjour
# affiche: Je suis le module bonjour et tu viens de mimporter
import bonjour
# affiche: rien
La bibliothèque standard est une collection de modules directement utilisables fournis à linstallation de Python.
Exemple : sys, random,
Toute la bibliothèque standard est documentée, et la traduction en Français est en cours :
[Link]
Mettez ce lien dans vos favoris, il vous sera très utile.
12.1 Couplage
12.1.1 Définition
12.1.2 Exemple
Ici on veut représenter des chats et des humains qui adoptent (ou non) des chats. Tous les chats ont un nom, et tous les
humains ont un prénom.
On peut utiliser pour cela deux classes : Chat et Humain.
class Chat:
def __init__(self, nom):
[Link] = nom
class Humain:
def __init__(self, prénom):
[Link]énom = prénom
alice = Humain(prénom="Alice")
print([Link]énom)
# affiche: Alice
Maintenant on veut que les humains puissent adopter des chats. Pour cela, on peut ajouter la méthode adopte() dans
la classe Humain.
65
Programmation en Python
class Humain:
def __init__(self, prénom):
[Link]énom = prénom
On peut accéder au nom du chat depuis la méthode adopte(), en utilisant la syntaxe [Link] vue précédem-
ment :
class Humain:
def __init__(self, prénom):
[Link]énom = prénom
12.1.3 Couplage
class Humain:
...
def adopte(self, chat):
print([Link]énom, "adopte", [Link])
Notez également que nous avons écrit [Link]. ainsi, la méthode adopte() ne peut être appelée que par une instance
qui a un attribut nom, sinon on aura une erreur.
Donc si on modifie la classe Chat et quon renomme lattribut nom en surnom par exemple, la méthode adopte() de
la classe Humain cessera de fonctionner : on dit quon a un couplage entre les classes Chat et Humain.
12.2 Composition
12.2.1 Définition
Exemple :
def calcule_x():
...
# du code ici
def fait_un_truc_avec_x(x):
...
# du code ici
x = calcule_x()
fait_un_truc_avec_x(x)
On voit que la fonction fait_un_truc_avec_x() prend un argument x, qui est retourné par la fonction calcule_x().
fait_un_truc_avec_x() doit donc être appelée après calcule_x(). On dit que fait_un_truc_avec_x() dépend
de calcule_x().
Un bon moyen dintroduire une dépendance entre deux classes est dutiliser les constructeurs.
Revoyons la classe Chat :
class Chat:
def __init__(self, nom):
[Link] = nom
Comme le constructeur de la classe Chat prend un nom en argument, il est impossible de construire des chats sans
nom :
chat = Chat()
# erreur: TypeError: __init__() missing 1 required positional argument: 'nom'
De la même façon, si on veut que tous les enfants aient un chat (pourquoi pas, après tout), on peut avoir une classe
Enfant, dont le constructeur prend une instance de chat en plus du prénom :
class Enfant:
def __init__(self, prénom, chat):
[Link]énom = prénom
[Link] = chat
alice = Enfant("Alice")
# erreur: TypeError: __init__() missing 1 required positional argument: 'chat'
12.2. Composition 67
Programmation en Python
Maintenant quon vit dans un monde où tous les enfants ont chacun un chat, on peut par exemple consoler tous les
enfants en leur demandant de caresser leur chat, chat qui va ronronner et faire plaisir à son propriétaire.
voici comment on peut coder cela : dabord, on rajoute les méthodes caresse() et ronronne() dans la classe chat :
class Chat:
def __init__(self, nom):
[Link] = nom
def ronronne(self):
print([Link], 'fait: "prrrrr"')
def caresse(self):
[Link]()
class Enfant:
def __init__(self, prénom, chat):
[Link]énom = prénom
[Link] = chat
def console(self):
[Link]()
Si on combine ces deux classes dans le même morceau de code et quon exécute les instructions suivantes :
Chapitre 13 - Bibliothèques
13.1 Introduction
Souvenez-vous, dans le chapitre 12, nous avons vu que le code suivant fonctionne sil y a un ficher [Link] quelque part
qui contient la fonction bar :
import foo
[Link]()
Ce fichier peut être présent soit dans le répertoire courant, soit dans la bibliothèque standard Python.
Vous connaissez peut-être le rôle de la variable denvironnement PATH. Celle-ci contient une liste de chemins, séparés
par le caractère : et est utilisée par votre shell pour trouver le chemin complet des commandes que vous lancez.
Par exemple :
PATH="/bin:/usr/bin:/usr/sbin"
$ ifconfig
# lance le binaire /usr/sbin/ifconfig
$ ls
# lance le binaire /bin/ls
Le chemin est ńărésoluăż par le shell en parcourant la liste de tout les chemins de la variable PATH, et en regardant si
le chemin complet existe. La résolution sarrête dès le premier chemin trouvé.
Par exemple, si vous avez PATH="/home/user/bin:/usr/bin" et un fichier ls dans /home/user/bin/ls, cest
cette commande-là (et non /bin/ls) qui sera utilisé quand vous taperez ls.
69
Programmation en Python
13.2 [Link]
En Python, il existe une variable path prédéfinie dans le module sys qui fonctionne de manière similaire à la variable
denvironnement PATH.
Si jessaye de lafficher sur ma machine, voici ce que jobtiens :
import sys
print([Link])
[
"",
"/usr/lib/python3.8",
"/usr/lib/python3.8/lib-dynload",
"/home/dmerej/.local/lib/python3.8/",
"/usr/lib/python3.8/site-packages",
]
Le résultat dépend :
— du système dexploitation ;
— de la façon dont Python a été installé ;
— et de la présence ou non de certains répertoires.
En fait, [Link] est construit dynamiquement par linterpréteur Python lors de son démarrage.
Notez également que [Link] commence par une chaîne vide. En pratique, cela signifie que le répertoire courant à
la priorité sur tout le reste.
Prenons un exemple. Si vous ouvrez un explorateur de fichiers dans le deuxième élément de la liste de [Link]
(/usr/lib/python3.8/ sur ma machine), vous trouverez un grand nombre de fichiers Python.
Notamment, vous devriez trouver un fichier [Link] dans ce répertoire.
En fait, vous trouverez la plupart des modules de la bibliothèque standard dans ce répertoire.
Maintenant, imaginons que vous avez un deuxième fichier [Link] dans votre répertoire courant. Finalement, ima-
ginez que vous lancez un fichier [Link] contentant import random dans ce même répertoire.
Et bien, cest le fichier [Link] de votre répertoire qui sera utilisé, et non celui de la bibliothèque standard !
Un autre aspect notable de [Link] est quil ne contient que deux répertoires dans lesquels lutilisateur courant peut
potentiellement écrire : le chemin courant et le chemin dans ~/.local/lib. Tous les autres (/usr/lib/python3.8/,
etc.) sont des chemins ńăsystèmeăż et ne peuvent être modifiés que par un compte administrateur (avec root ou sudo,
donc).
La situation est semblable sur macOS et Windows.
Prenons un exemple :
# dans [Link]
import tabulate
scores = [
["John", 345],
["Mary-Jane", 2],
["Bob", 543],
]
table = [Link](scores)
print(table)
$ python3 [Link]
--------- ---
John 345
Mary-Jane 2
Bob 543
--------- ---
Ici, le module tabulate nest ni dans la bibliothèque standard, ni écrit par lauteur du script [Link]. On dit que cest
une bibliothèque tierce.
On peut trouver le code source de tabulate facilement. La question qui se pose alors est : Comment faire en sorte que
[Link] contienne le module tabulate ?
Eh bien, plusieurs solutions soffrent à vous.
Si vous utilisez une distribution Linux, peut-être pourrez-vous utiliser votre gestionnaire de paquets :
Comme vous lancez votre gestionnaire de paquets avec sudo, celui-ci sera capable décrire dans les chemins système
de [Link].
13.3.2 À la main
Une autre méthode consiste à partir des sources : Par exemple, si le paquet de votre distribution nest pas assez récent,
ou si vous avez besoin de modifier le code de la bibliothèque en question.
Voici une marche à suivre possible :
1. Récupérer les sources de la version qui vous intéresse dans la section téléchargement de bitbucket ;
2. Extraire larchive, par exemple dans src/tabulate ;
3. Se rendre dans src/tabulate et lancer python3 [Link] install --user.
La plupart des bibliothèques Python contiennent un [Link] à la racine de leurs sources. Il sert à plein de choses, la
commande install nétant quune parmi dautres.
Le fichier [Link] contient en général simplement un import de setuptools, et un appel à la fonction setup(),
avec de nombreux arguments :
# tabulate/[Link]
from setuptools import setup
setup(
name='tabulate',
version='0.8.1',
description='Pretty-print tabular data',
py_modules=["tabulate"],
scripts=["bin/tabulate"],
...
)
Par défaut, [Link] essaiera décrire dans un des chemins système de [Link], doù lutilisation de loption --user.
Voici à quoi ressemble la sortie de la commande :
$ cd src/tabulate
$ python3 [Link] install --user
running install
...
Copying [Link] to /home/dmerej/.local/lib/python3.7/site-packages
...
Installing tabulate script to /home/dmerej/.local/bin
Notez que le module a été copié dans ~/.local/lib/python3.7/site-packages/ et le script dans ~/.local/bin.
Cela signifie que tous les scripts Python lancés par lutilisateur courant auront accès au module tabulate.
Notez également quun script a été installé dans ~/.local/bin. Une bibliothèque Python peut contenir aussi bien des
modules que des scripts.
Un point important est que vous navez en général pas besoin de lancer le script directement. Vous pouvez utiliser
python3 -m tabulate. Procéder de cette façon est intéressant puisque vous navez pas à vous soucier de rajouter le
chemin dinstallation des scripts dans la variable denvironnement PATH.
13.4 Dépendances
import cli_ui
headers=["name", "score"]
data = [
[(bold, "John"), (green, 10.0)],
[(bold, "Jane"), (green, 5.0)],
]
cli_ui.info_table(data, headers=headers)
Pour ce faire, elle repose sur la bibliothèque tabulate vue précédemment. On dit que cli-ui dépend de tabulate.
La déclaration de la dépendance de cli-ui vers tabulate seffectue également dans le fichier [Link] :
setup(
name="cli-ui",
version="0.9.1",
install_requires=[
"tabulate",
...
],
...
)
13.4.2 [Link]
On comprend dès lors quil doit nécessairement exister un annuaire permettant de relier les noms de dépendances à leur
code source.
Cet annuaire, cest le site [Link]. Vous y trouverez les pages correspondant à tabulate et cli-ui.
13.4.3 pip
pip est un outil qui est fréquemment installé en même temps que linterpréteur Python. Vous pouvez également linstaller
séparément grâce au script [Link], en lançant python3 [Link] --user.
Il est conseillé de toujours lancer pip avec python3 -m pip. De cette façon, vous êtes certains dutiliser le module
pip correspondant à votre binaire python3, et vous ne dépendez pas de ce quil y a dans votre PATH.
pip est capable dinterroger le site [Link] pour retrouver les dépendances, et également de lancer les différents
scripts [Link].
Comme de nombreux outils, il sutilise à laide de commandes. Voici comment installer cli-ui à laide de la commande
install de pip :
13.4. Dépendances 73
Programmation en Python
On y retrouve les bibliothèques cli-ui et tabulate, bien sûr, mais aussi la bibliothèque gaupol, qui correspond au
programme dédition de sous-titres que jai installé à laide du gestionnaire de paquets de ma distribution. Précisons que
les modules de la bibliothèque standard et ceux utilisés directement par pip sont omis de la liste.
On constate également que chaque bibliothèque possède un numéro de version.
Les numéros de version remplissent plusieurs rôles, mais lun des principaux est de spécifier des changements incom-
patibles.
Par exemple, pour cli-ui, la façon dappeler la fonction ask_choice a changé entre les versions 0.7 et 0.8, comme le
montre cet extrait du changelog :
The list of choices used by ask_choice is now a named keyword argument :
Plusieurs possibilités :
— On peut bien sûr adapter le code pour utiliser la nouvelle API, mais cela nest pas toujours possible ni souhaitable.
— Une autre solution est de spécifier des contraintes sur le numéro de version dans la déclaration des dépendances.
Par exemple :
setup(
install_requires=[
"cli-ui < 0.8",
...
]
)
Souvenez-vous que les fichiers systèmes sont contrôlés par votre gestionnaire de paquets.
Les mainteneurs de votre distribution font en sorte quils fonctionnent bien les uns avec les autres. Par exemple, le paquet
python3-cli-ui ne sera mis à jour que lorsque tous les paquets qui en dépendent seront prêts à utiliser la nouvelle
API.
En revanche, si vous lancez sudo pip (où pip avec un compte root), vous allez écrire dans ces mêmes répertoire et
vous risquez de ńăcasserăż certains programmes de votre système.
Mais il y a un autre problème encore pire
Supposons deux projets A et B dans votre répertoire personnel. Ils dépendent tous les deux de cli-ui, mais lun des
deux utilise cli-ui 0.7 et lautre cli-ui 0.9. Que faire ?
La solution est dutiliser un environnement virtuel (virtualenv en abrégé). Cest un répertoire isolé du reste du système.
La commande python3 -m venv fonctionne en général partout, dès linstallation de Python3 (out of the box, en Anglais),
sauf sur Debian et ses dérivées.
Si vous utilisez Debian, la commande pourrait ne pas fonctionner. En fonction des messages derreur que vous obtenez,
il est possible de résoudre le problème en :
— installant le paquet python3-venv ;
— ou en utilisant dabord pip pour installer virtualenv, avec python3 -m pip install virtualenv
--user puis en lançant python3 -m virtualenv foo-venv.
À noter :
— Le répertoire ńăglobalăż dans ~/.local/lib a disparu.
— Seuls quelques répertoires systèmes sont présents (ils correspondent plus ou moins à lemplacement des modules
de la bibliothèque standard).
— Un répertoire au sein du virtualenv a été ajouté.
Ainsi, lisolation du virtualenv est reflété dans la différence de la valeur de [Link].
Il faut aussi préciser que le virtualenv nest pas complètement isolé du reste du système. En particulier, il dépend encore
du binaire Python utilisé pour le créer.
Par exemple, si vous utilisez /usr/local/bin/python3.7 -m venv foo-37, le virtualenv dans foo-37 utilisera
Python 3.7 et fonctionnera tant que le binaire /usr/local/bin/python3.7 existe.
Cela signifie également quil est possible quen mettant à jour le paquet python3 sur votre distribution, vous rendiez
inutilisables les virtualenvs créés avec lancienne version du paquet.
Daprès ce qui précède, le virtualenv ne devrait contenir aucun module en dehors de la bibliothèque standard et de pip
lui-même.
On peut sen assurer en lançant python3 -m pip freeze depuis le virtualenv et en vérifiant que rien ne saffiche :
On peut alors utiliser le module pip du virtualenv pour installer des bibliothèques dans celui-ci :
Cette fois, aucune bibliothèque nest marquée comme déjà installée, et on récupère donc cli-ui et toutes ses dépen-
dances.
On a enfin notre solution pour résoudre notre conflit de dépendances : On peut simplement créer un virtualenv par
projet. Ceci nous permettra davoir effectivement deux versions différentes de cli-ui, isolées les unes des autres.
Devoir préciser le chemin du virtualenv en entier pour chaque commande peut devenir fastidieux ; heureusement, il est
possible dactiver un virtualenv, en lançant une des commandes suivantes :
— source foo-venv/bin/activate, si vous utilisez un shell POSIX ;
— source foo-venv/bin/[Link], si vous utilisez Fish ;
— foo-venv\bin\[Link], sous Windows.
Une fois le virtualenv activé, taper python, python3 ou pip utilisera les binaires correspondants dans le virtualenv
automatiquement, et ce, tant que la session du shell sera ouverte.
Le script dactivation ne fait en réalité pas grand-chose à part modifier la variable PATH et rajouter le nom du virtualenv
au début de linvite de commandes :
# Avant
user@host:~/src $ source foo-env/bin/activate
# Après
(foo-env) user@host:~/src $
Le système de gestions des dépendances de Python peut paraître compliqué et bizarre, surtout venant dautres langages.
Mon conseil est de toujours suivre ces deux règles :
— un virtualenv par projet ;
— toujours utiliser pip depuis un virtualenv.
Certes, cela peut paraître fastidieux, mais cest une méthode qui vous évitera probablement de vous arracher les cheveux
(croyez-en mon expérience).
Voici un exemple darborescence qui montre deux projets, projet-1 et projet-2, chacun avec son virtualenv dédié :
projets
projet-1
projet_1.py
.venv
bin
include
lib
...
projet-2
projet_2.py
.venv
bin
include
lib
...
Notez bien que les sources de chaque projet projet_1.py et projet_2.py sont au même niveau que le répertoire
.venv et non à lintérieur de celui-ci.
Vous pouvez aussi retenir la règle suivante : étant donné un répertoire ńăXăż contenant les sources dun projet, les
commandes à lancer pour créer le virtualenv seront :
$ cd X/
$ python3 -m venv .venv
On vous a peut-être déjà dit que linformatique consiste à manipuler des suites de 0 et et de 1, mais quen est-il exacte-
ment ?
De manière surprenante, la réponse à cette question va nous emmener sur un chemin qui va parler de maths, puis de
comment le langage Python peut interagir avec ńălextérieurăż.
Si je vous parle de ce que représente le texte 342 vous pouvez le voir de deux façons :
1. Cest une suite de chiffres : 3, puis 4, puis 2 ;
2. Cest un nombre (quelque part entre 300 et 350).
On se sert des chiffres de 0 à 9 pour représenter des nombres.
14.1.2 La base 10
Plus exactement, pour passer de la suite de chiffres 342 au nombre, on part de la fin, puis on ajoute chaque chiffre,
multiplié par la puissance de 10 adéquate :
2 * 1
+ 4 * 10
+ 2 * 10 * 10
soit :
2
+ 40
+ 300
79
Programmation en Python
14.1.3 La base 16
En informatique, on se sert souvent de la base 16. Cest le même principe : on se sert des ńăchiffresăż de 0 à F (A vaut
10, B vaut 11, jusquà F qui vaut 15).
Ainsi, la suite DA2 peut être interprétée comme suit :
2 * 1
+ 10 * 16
+ 13 * 16 * 16
soit :
2
+ 160
+ 3328
soit 3746.
On appelle aussi la base 16 la base hexadécimale, ou hexa en abrégé.
14.1.4 La base 2
0 * 1
+ 1 * 2
+ 1 * 2 * 2
soit :
0
+ 2
+ 4
soit 6.
14.1.6 À retenir
Ces paquets de 8 ne veulent rien dire en eux-mêmes. Ils nont de sens que dans le cadre dune convention.
Par exemple, loctet 10100100 peut être un nombre écrit en binaire (164 en loccurrence), mais peut avoir une toute autre
signification.
Le nombre de valeurs possible augmente très rapidement avec le nombre doctets :
— 1 octet, cest 255 valeurs possibles (2 ** 8) ;
— 2 octets, cest 65.536 valeurs possibles (2 ** 16) ;
— 4 octets, cest [Link] valeurs possibles (2 ** 32).
On se sert des préfixes 0b et 0x pour noter les nombres en base binaire ou hexadécimale respectivement :
print(0b1110)
# affiche: 6
print(0xDA2)
# affiche: 3490
x = 0b0010010 # 18
y = 0b0010011 # 19
z = 0b1010010 # 82
Notez que le premier bit est plus ńăfortăż que le dernier. On passe de 18 à 82 quand le premier bit passe de 0 à 1. À
linverse, on passe seulement de 18 à 19 quand le dernier bit passe de 0 à 1. On dit quon a utilisé un mode Petit-boutisme
(ńălittle endianăż en Anglais).
Voir aussi :
La page wikipedia consacée au boutisme.
On peut construire des listes doctets en utilisant bytearray et une liste de nombres :
data = bytearray(
[0b1100001,
0b1100010,
0b1100011
]
)
# equivalent:
data = bytearray([97, 98, 99])
14.1.10 Texte
On peut aussi interpréter les octets comme du texte : Cest la table ASCII.
La table se lit ainsi : si on veut connaître la suite de 0 et de 1 qui correspond à B, on lit les 3 premiers bits de haut en
bas sur la colonne 100, puis les 4 bits sur la ligne 0010. Du coup B sécrit en 7 bits 1000010, soit 66 en décimal, et 42
en hexadécimal.
x = chr(0x42)
print(x)
# affiche: B
x = ord('B')
print(x)
# affiche: 66
Python utilise ASCII pour afficher les bytearrays si les caractères sont ńăimprimablesăż :
Note : Notez que Python rajoute quelque chose qui ressemble à un appel de fonction lorsquil affiche le bytearray : ce
nest pas un vrai appel de fonction.
Notez bien que ce quaffiche Python nest quune interpétation dune séquence doctets.
14.1.14 Types
La variable b"abc" est une ńăchaîne doctetsăż, de même que "abc" est une ńăchaîne de caractèresăż.
Python appelle ces types bytes et str :
print(type("abc"))
# affiche: str
print(type(b"abc"))
# affiche: bytes
De la même manière quon ne peut pas modifier un caractère à lintérieur dune string, on ne peut pas modifier un bit, ou
un octet dans une variable de type bytes :
a = "foo"
# a[0] = "f" => TypeError: 'str' object does not support item assignment
b = b"foo"
# b[0] = 1 => TypeError: 'bytes' object does not support item assignment
b = bytearray(b"foo")
b[0] = 103
print(b)
# affiche: bytearray(b"goo")
text = "chaise"
encodé = [Link]("ascii")
print(encodé)
# affiche: b"chaise"
bytes = b"table"
décodé = [Link]("ascii")
print(décodé)
# affiche: b"table"
Notez que dans le deuxième exemple, on est bien en train de ńădécoderăż un paquet de 0 et de 1. Il peut sécrire ainsi :
bytes = b"\x74\x61\x62\x6c\x65"
décodé = [Link]("ascii")
print(décodé)
# affiche: table
Vous avez sûrement remarquer quil ny a pas de caractères accentués dans ASCII. Du coup, il existe dautres conventions
quon appelle ńăencodageăż.
On peut spécifier lencodage quand on appelle la méthode decode() :
Notez que la même suite doctets a donné des résultats différents en fonction de lencodage !
14.1.18 Unicode
encodé = "abc".encode("utf-8")
print(encodé)
# affiche: b'abc'
encodé = "café".encode("utf-8")
print(encodé)
# affiche: b'caf\xc3\xa9"
Enfin, certains caractères (comme les emojis) sont représentés par 3 voire plus octets.
Avertissement : Toutes les séquences doctets ne sont pas forcément valides quand on veut les décoder en UTF-8.
14.1.20 Conséquences
— On peut représenter tout type de texte avec UTF-8 (latin, chinois, coréen, langues disparues, ).
— On ne peut pas accéder à la n-ième lettre directement dans une chaîne encodée en UTF-8, il faut parcourir lettre
par lettre (ce qui en pratique est rarement un problème).
14.2 Fichiers
Cela vous est peut-être déjà arrivé : Imaginons que vous ayez dans votre répertoire courant un code source python dans
mon_script.py et un pdf dans [Link].
Vous pourrez ouvrir mon_script.py dans un éditeur de texte, mais pas [Link], ou alors ça affichera nimporte
quoi.
En Python, on utilise la fonction native open(), en passant en argument le chemin du fichier.
Selon que lon veuille accéder au texte dans le fichier ou aux données binaires qui sont à lintérieur, on utilisera largument
"r" ou "rb" (r comme read, et b comme binary)
Enfin, open() renvoie un ńăfile-objetăż, quon note souvent f, qui contient une méthode read() pour lire le contenu
du fichier.
En pratique, voici ce que cela donne :
f = open("mon_script.py", "r")
code = [Link]()
print(code)
# affiche le code présent dans le fichier mon_script.py
f = open("[Link]", "rb")
données = [Link]()
# données est maintenant une grosse suite
# d'octets
f = open("[Link]", "r")
[Link]()
# Erreur: UnicodeDecodeError: 'utf-8' codec can't
# decode byte 0xd0 in position 10
Comme on doit utilisé loption rb pour lire le pdf, on dit parfois que le fichier pdf est un fichier ńăbinaireăż, par
opposition avec mon_script.py qui est un fichier ńătexteăż.
Ça na pas vraiment de sens : les deux fichiers sont stockés sur votre ordinateur comme des suites doctets, indépendam-
ment de leur contenu.
Il se trouve que lun des deux contient une suite doctets qui est décodable en tant que string. En fait, sous le capot,
la suite doctets renvoyée dans le premier example a été décodée avec lencodage par défaut de votre système. On peut
dailleurs passer lencodage en argument à open() :
On peut aussi écrire dans un fichier, toujours avec open(), mais cette fois-ci avec la méthode write() du file-objet.
On peut écrire du texte avec loption "w" et une chaîne de caractères :
f = open("mon_script.py", "w")
[Link]("Nouveau contenu!")
Et écrire directement des données binaires avec "wb" et une suite doctets
f = open("[Link]", "wb")
[Link](b"\x0c\x1f...")
Encore une fois, sous le capot, la chaîne de caractères sera encodée par Python avant dêtre écrite dans le fichier texte.
Notez quil est impératif de fermer les fichiers que vous ouvrez, que ce soit en lecture ou en écriture, en appelant la
méthode close() :
f = open("mon_poé[Link]", "w")
[Link](premier_vers)
[Link](deuxième_vers)
[Link]()
14.2.4 Conseils
14.2. Fichiers 87
Programmation en Python
15.1 Démarrage
Jusquici, on a toujours lancé la commande Python avec python suivi du nom dun fichier source.
Il est égalemnet possible de lancer la commande python3 sans argument.
Dans ce cas, on se retrouve avec une autre invite de commandes :
$ python3
Python 3.7.1 (default, Oct 22 2018, 10:41:28)
[GCC 8.2.1 20180831] on linux
Type "help", "credits" or "license" for more information.
>>>
89
Programmation en Python
Notez les trois chevrons : >>>. Cela vous permet de différencier linvite de commandes du système dexploitation de
celle de Python.
— Système dexploitation -> Python : taper python3 (sans arguments).
— Python -> Système dexploitation : taper quit().
>>> a = 42
>>> a
42
>>> b = 4
>>> a + b
46
# dans [Link]
salutation = "Bonjour,"
def dire_bonjour(nom):
print(salutation, nom)
On peut démarrer un interpréteur interactif dans le répertoire contenant le fichier bonjour`.py, et ńăjouerăż avec les
attributs du module bonjour :
Avertissement : Si le contenu de [Link] change, il faut relancer linterpréteur interactif et refaire limport.
Chapitre 16 - Héritage
Dans un chapitre précédent, on a parlé de composition qui décrit une classe à lintérieur dune autre classe.
Pour rappel :
class Chat:
def __init__(self, nom):
[Link] = nom
def ronronne(self):
print([Link], 'fait: "prrrrr"')
def caresse(self):
[Link]()
class Enfant:
def __init__(self, prénom, chat):
[Link] = chat
def console(self):
[Link]()
93
Programmation en Python
16.2 Vocabulaire
Ici on va parler dhéritage, qui décrit une autre relation entre classes, appelée parfois un peu abusivement ńăpartage de
codeăż.
Pour indiquer quune classe B hérite dune classe A, on écrit A dans des parenthèses au moment de déclarer la classe B :
class A:
...
class B(A):
...
16.3 Utilisation
Si une méthode nest pas trouvée dans la classe courante, Python ira la chercher dans la classe parente :
class A:
def méthode_dans_a(self):
print("dans A")
class B(A):
def méthode_dans_b(self):
print("dans B")
b = B()
b.méthode_dans_b()
# Affiche: 'dans B', comme d'habitude
b.méthode_dans_a()
# Affiche: 'dans A'
Sil y a plusieurs classes parentes, Python les remonte toutes une à une. On dit aussi quil y a une hiérarchie de classes :
class A:
def méthode_dans_a(self):
print("dans A")
class B(A):
def méthode_dans_b(self):
print("dans B")
c = C()
c.méthode_dans_a()
# affiche: 'dans A'
class A:
def __init__(self):
print("initialisation de A")
class B(A):
...
b = B()
# affiche: "initialisation de A"
16.6 Attributs
class A:
def __init__(self):
self.attribut_de_a = 42
class B(A):
...
b = B()
print(b.attribut_de_a)
# affiche: 42
16.7 Surcharge
class A:
def une_méthode(self):
print("je viens de la classe A")
class B(A):
(suite sur la page suivante)
b = B()
b.une_méthode()
# affiche: "je viens de la classe B'
16.8 super()
On peut utiliser super() pour chercher explicitement une méthode dans la classe parente :
class A:
def une_méthode(self):
print("je viens de la classe A")
class B(A):
def une_méthode(self):
super().une_méthode()
print("je viens de la classe B")
b = B()
b.une_méthode()
# affiche:
# je viens de la classe A
# je viens de la classe B
class A:
def __init__(self):
self.attribut_de_a = "bonjour"
class B(A):
def __init__(self):
self.attribut_de_b = 42
b = B()
print(b.attribut_de_b)
# affiche: 42
print(b.attribut_de_a)
# erreur: AttributeError
class A:
def __init__(self):
self.attribut_de_a = "bonjour"
class B(A):
def __init__(self):
super().__init__()
self.attribut_de_b = 42
b = B()
print(b.attribut_de_b)
# affiche: 42
print(b.attribut_de_a)
# affiche: "bonjour"
Chapitre 17 - Décorateurs
17.1.1 Introduction
# Définition de la fonction:
def dire_bonjour(nom):
print("Bonjour " + nom)
# Appel
dire_bonjour("Max")
99
Programmation en Python
Il se trouve quen Python, on peut assigner des fonctions à des variables. Cest différent dassigner le résultat de lappel à
une fonction à une variable, et ça permet de retarder lappel :
De façon cruciale, notez que lon na pas mis de parenthèses à droite lorsquon a créé la variable
ma_fonction_qui_dit_bonjour.
On peut donc dire que lorsquon définit une fonction avec def ma_fonction() et un corps, il y a en réalité deux
étapes :
1. Python stocke le corps de la fonction quelque part ;
2. Il assigne le corps de celle-ci à une variable dont le nom est ma_fonction.
En Python, il est assez fréquent dutiliser un code tel que celui-ci, souvent avec un dictionnaire :
fonctions_connues = {
"français": dire_bonjour_en_français,
"anglais": dire_bonjour_en_anglais,
}
if langue_parlée in fonctions_connues:
fonction = fonctions_connues[langue_parlée]
fonction(prénom)
def appelle_deux_fois(f):
f()
f()
def crier():
print("Aline !")
appelle_deux_fois(crier)
# affiche:
# Aline !
# Aline !
def affiche_message(message):
def affiche():
print(message)
affiche()
affiche_message("Bonjour")
# affiche: Bonjour
def affiche_message(message):
def affiche():
print(message)
affiche()
# NameError: 'affiche' is not defined
En réalité, on combine souvent les closures avec des fonctions qui retournent dautres fonctions :
def fabrique_fonction_qui_additionne(n):
def fonction_résultat(x):
return x + n
return fonction_résultat
additionne_2 = fabrique_fonction_qui_additionne(2)
y = additionne_2(5)
print(y)
# affiche: 7
Le fait quon puisse traiter les fonctions comme nimporte quelle autre valeur (cest-à-dire les assigner à des variables,
les passer en argument et les retourner), est caractéristique des langages dits ńăfonctionnelsăż. Python est donc à la fois
un langage impératif, objet et fonctionnel. On dit que cest un langage multi-paradigmes.
17.2 Décorateurs
17.2.1 Définition
def mon_décorateur(fonction):
def fonction_retournée():
# fait quelque chose avec l'argument `fonction`, par exemple
# l'appeler avec un argument:
fonction(42)
return fonction_retournée
@mon_décorateur
def ma_fonction_décorée(un_argument):
fais_un_truc_avec(un_argument)
def ma_fonction_décorée(un_argument):
fais_un_truc_avec(un_argument)
ma_fonction_décorée = mon_décorateur(ma_fonction_décorée)
On peut faire un décorateur qui nous empêche dappeler une fonction sous certaines conditions :
def pas_pendant_la_nuit(fonction):
def résultat():
if il_fait_nuit:
print("chut")
else:
fonction()
return résultat
@pas_pendant_la_nuit
def dire_bonjour():
print("bonjour")
il_fait_nuit = True
dire_bonjour()
# affiche: "chut"
On peut aussi avoir des arguments passés aux décorateurs. Dans ce cas, on a besoin de trois fonctions imbriquées. En
effet, il nous faut une fonction pour traiter largument message et une autre pour traiter largument fonction :
def affiche_message_avant_appel(message):
def décorateur(fonction):
def résultat():
print(message)
fonction()
return résultat
return décorateur
dire_bonjour()
# affiche:
# dire_bonjour est appelée
# bonjour
Chapitre 18 - Exceptions
On a souvent montré des exemples de code qui provoquaient des erreurs au cours des chapitres précédents. Il est temps
maintenant de se pencher davantage sur celles-ci.
Reprenons un exemple de code qui provoque une erreur, par exemple en essayant de diviser par zéro :
def mauvaise_fonction():
return 1 / 0
def fonction_intermédiaire():
mauvaise_fonction()
def fonction_principale():
fonction_intermédiaire()
fonction_principale()
105
Programmation en Python
Ceci est une pile dappels. Elle permet de voir exactement par quelles fonctions on est passé et dans quel ordre. Elle se
lit de haut en bas :
— on a appelé fonction_principale() ;
— cette fonction a à son tour appelé fonction_intermédiaire() ;
— fonction_intermédiaire() a appelé mauvaise_fonction() ;
— mauvaise_fonction() a levé une exception.
Notez que chaque élément de la pile comprend :
— le nom de la fonction ;
— le chemin du module la contenant ;
— le numéro et la ligne précise du code qui a été appelé .
Il est important de bien lire les piles dappels quand on cherche à comprendre doù vient une exception.
Après la pile dappels, on a le nom de lexception et sa description.
Les exceptions sont toujours des instances de classes, et les classes dexceptions héritent toujours de la classe
BaseException.
Le nom de lexception est en réalité le nom de la classe, ici lexception levée par la ligne return 1 / 0 est une instance
de la classe ZeroDivisionError.
Cette exception fait partie des nombreuses exceptions prédéfinies en Python. Ensemble, elles forment une hiérarchie
dexceptions dont voici un extrait :
BaseException
+-- SystemExit
+-- KeyboardInterrupt
+-- Exception
+-- ArithmeticError
| +-- ZeroDivisionError
+-- LookupError
| +-- IndexError
| +-- KeyError
+-- OSError
| +-- FileNotFoundError
+-- TypeError
+-- ValueError
IndexError est levée quand on essaye daccéder à un index trop grand dans une liste :
ma_liste = ["pomme"]
ma_liste[2] = "abricot"
KeyError est levée quand on essaye daccéder à une clé qui nexiste pas dans un dictionnaire :
scores = { "Alice" : 10 }
score_de_bob = scores["Bob"]
(suite sur la page suivante)
# KeyError: 'Bob'
18.2.2 ValueError
ValueError est levée (entre autres) quand on tente une mauvaise conversion :
18.2.3 KeyboardInterrupt
18.2.4 FileNotFoundError
FileNotFoundError est levée quand on essaye douvrir, en lecture, un fichier qui nexiste pas :
On peut gérer (ou attraper) une exception en utilisant un bloc try/except et le nom dune classe dexception :
try:
a = 1 / 0
except ZeroDivisionError:
print("Quelqu'un a essayé de diviser par zéro!")
À noter : Le bloc dans try sinterrompt dès quune exception est levée, et on ne passe dans le bloc except que si une
exception est effectivement levée.
x = 14
y = 0
try:
z = x / y
print("z vaut", z)
except ZeroDivisionError:
print("Quelqu'un a essayé de diviser par zéro!")
x = 14
y = 2
try:
z = x / y
print("z vaut", z)
except ZeroDivisionError:
print("Quelqu'un a essayé de diviser par zéro!")
Notez que la ligne print("Quelqu'un a essayé de diviser par zéro!") na pas été exécutée.
Le mot après except doit être celui dune classe, et lexception nest gérée que si sa classe est égale ou une fille de
celle-ci.
Par exemple, ceci fonctionne car ZeroDivisionError est bien une fille de la classe ArithmeticError :
x = 14
y = 0
try:
z = x / y
print("z vaut", z)
except ArithmeticError:
print("Quelqu'un a essayé une opération impossible")
try:
tente_un_truc_risqué()
except ZeroDivisionError:
print("raté : division par zéro!")
except FileNotFoundError:
print("raté : fichier non trouvé")
try:
tente_un_truc_risqué()
except (ZeroDivisionError, FileNotFoundError)
print("raté!")
try:
ouvrir_fichier()
except FileNotFoundError as e:
print("le fichier: ", [Link], "n'existe pas")
Ici on utilise lattribut filename de la classe FileNotFoundError pour afficher un message derreur.
18.4.1 raise
On peut lever explicitement une exception en appelant le mot-clé raise suivi dune instance dune classe.
Par exemple en utilisant une exception native :
def dire_bonjour(prénom):
if not prénom:
raise ValueError("prénom vide")
class OpérationImpossible(Exception):
pass
def ma_fonction():
if cas_impossible:
raise OpérationImpossible()
try:
tente_un_truc_risqué()
except ArithmeticError:
...
raise
18.5.1 else
Si on rajoute un bloc else après le except, le bloc nest éxécuté que si aucune exception na été levée :
try:
tente_un_truc_risqué()
except (ZeroDivisionError, FileNotFoundError):
print("raté")
else:
print("ouf, ça a marché")
18.5.2 finally
Si on rajoute un bloc finally après le except, le bloc est exécuté dans tous les cas, quune exception ait été levé ou
non. On sen sert souvent pour ńăannulerăż du code qui aurait été utilisé dans le bloc try :
personnage = Personnage()
try:
personnage.entre_en_scène()
personnage.tente_un_truc_risqué()
except ZeroDivisionError:
print("raté")
finally:
personnage.quitte_la_scène()
Si dans le bloc try une exception différente de ZeroDivisionError est levée, on passera quand même dans le bloc
finally, puis lexception sera levée à nouveau.
TODO : exemple plus complet
Chapitre 19 - Objets
On a déjà parlé de programmation orientée objet en Python, mais pour linstant, on a vu que des classes et des instances.
Or, le concept dobjet existe bel et bien en Python, et il est plus que temps de vous le présenter.
Mais avant, il faut faire un petit détour par les attributs et méthodes de classes.
19.1.1 Rappels
class MaClasse
def ma_méthode(self):
print(self.mon_attribut)
Le bloc indenté en-dessous du mot-clé class sappelle le corps de la classe. Et les méthodes sont définies avec le
mot-clé def dans le corps de la classe.
On dit que ce sont des méthodes dinstance parce quil faut créer une instance pour pouvoir les appeler :
mon_instance = MaClasse()
mon_instance.ma_méthode()
111
Programmation en Python
class MaClasse:
mon_attribut_de_classe = 42
Ici mon_attribut_de_classe existe à la fois dans les instances de MaClasse et dans la classe elle-même :
print(MaClasse.mon_attribut_de_classe)
# affiche: 42
mon_instance = MaClasse()
print(mon_instance.mon_attribut_de_classe)
# affiche: 42
Un point important est que les attributs de classe sont partagés entre toutes les instances. Voici un exemple dutilisation
possible :
class Voiture:
nombre_total_de_voitures_fabriquées = 0
Notez que pour changer lattribut de classe depuis une méthode, (comme dans la méthode __init__ ci-dessus) on
utilise le nom de la classe directement, et non pas self.
class Voiture:
nombre_total_de_voitures_fabriquées = 0
@classmethod
def fabrique_ferrari(cls):
return cls("ferrari", "rouge")
ferrari = Voiture.fabrique_ferrari()
Détaillons ce quil se passe sur la dernière ligne : à gauche du signe égal, il y a une variable, et à droite une expression
(Voiture.fabrique_ferrari())
Lexpression est constitué dune classe à gauche du point (Voiture), et dun attribut à droite du point
fabrique_ferrari, suivi de parenthèses.
Comme fabrique_ferrari est une méthode de classe, on va appeler la méthode de classe fabrique_ferrari en
lui passant la classe courante en argument.
On arrive ainsi dans le corps de la méthode de classe fabrique_ferrari, et cls vaut la classe Voiture.
Finalement, on évalue lexpression cls("ferrari", rouge") en remplaçant cls par sa valeur, ce qui donne
Voiture("ferrari", "rouge") qui correspond bien à ce quon obtient : Une instance de la classe Voiture.
19.2 Objets
En fait, tout ce quon manipule en Python est un objet. Et tous les objets sont toujours des instances dune classe. On
peut accéder à la classe qui a servi à instancier un objet avec la fonction type, par exemple :
class MaClasse:
pass
mon_instance = MaClasse()
print(type(mon_instance))
# affiche:
# <class '__main__.MaClasse'>
Mais aussi :
print(type(2))
# affiche: int
print(type("bonjour"))
# affiche: str
Donc en Python, les entiers sont des instances de la classe int, et les strings des instances de la classe str.
Ainsi, vous pouvez voir lexpression x = str(y) de deux façons :
— Soit on appelle la fonction native str pour convertir y en string.
— Soit on crée une nouvelle instance de la classe str en appelant le constructeur de la classe str avec y en
argument.
Notez que ça ne sarrète pas là :
def ma_fonction():
pass
(suite sur la page suivante)
print(type(ma_fonction))
# affiche: function
class MaClasse:
def ma_méthode(self):
pass
mon_instance = MaClasse()
print(type(mon_instance.ma_méthode))
# affiche: method
import sys
print(type(sys))
# affiche: module
Et même :
print(type(MaClasse))
# affiche: type
print(type(type))
# affiche: type
Et oui, les classes elles-mêmes sont des instances de classe ! (de la classe type)
Du coup en Python, le terme objet désigne toujours une instance de classe, même None est une instance dune classe
(elle sappelle NoneType).
Il est temps de revenir sur lévaluation des expressions impliquant des attributs.
On a vu trois systèmes différents :
Appeler une fonction définie dans un module :
import mon_module
mon_module.ma_fonction()
mon_instance = MaClasse()
mon_instance.ma_méthode()
MaClasse.ma_méthod_de_classe()
Daprès ce quon a vu dans la section précédente, on sait maintenant que dans tous les cas, à gauche du point se situe un
objet, et que tous les objets sont des instances dune classe (appelé le ńătypeăż de lobjet).
Pour évaluer lexpression mon_objet.mon_attribut, où mon_objet est une instance de mon_type, Python cherche
toujours lattribut dans deux endroits :
— dabord en tant quattribut de linstance mon_objet ;
class A:
def f1(self):
print("f1 dans A")
def f2(self):
print("f2")
class B(A):
@classmethod
def f3(cls):
print("f3")
def f1(self):
print("f1 dans B")
b = B()
b.f1()
b.f3()
b.f2()
# affiche:
# f1 dans B
# f3
# f2
19.2.2 Conclusion
Maintenant, vous devriez comprendre pourquoi on dit parfois quen Python, tout est objet.
Dans un prochain chapitre, on expliquera pourquoi, en plus de cela, on peut dire quen Python, tout est dictionnaire.