info-f-101 programmation transparents du cours · ... how to think like a computer scientist ,...
TRANSCRIPT
INFO-F-101
Programmation
-
Transparents du cours
Thierry Massart
Université Libre de Bruxelles
Facultés des Sciences
Première année de Bachelier en Informatique
Septembre 2011
Remarque importante :
Ce syllabus reprend, avec l’accord de l’auteur,des “transparents” du cours Programmation et Algorithmique I
du Professeur Hadrien Mélotde l’Institut d’Informatique à L’Université de Mons.
Je le remercie pour m’avoir accordé cette autorisation
Chapitres du cours1 Introduction2 Types, variables et expressions3 Fonctions4 Instructions conditionnelles5 Itération et chaînes de caractères6 Listes7 Dictionnaires8 Tuples9 Récursivité10 Prouver les propriétés des algorithmes11 Complexité : évaluer l’efficacité des algorithmes12 Fichiers et exceptions13 Introduction aux objets14 Introduction aux Structures de Données
Chapitre 1. Introduction
Contenu du chapitre
1 Objectifs du cours
2 Organisation pratique
3 Algorithmes et programmes
4 Erreurs (bugs)
5 Glossaire
Programmation (Chap. 1) Introduction 3 / 33
Objectifs du cours
Objectifs du cours
Programmation (Chap. 1) Introduction 4 / 33
Objectifs du cours
Objectifs principaux du cours
Etre capable de
comprendre un algorithme
concevoir un algorithme (efficace)
écrire un programme
Programmation (Chap. 1) Introduction 5 / 33
Organisation pratique
Organisation pratique
Programmation (Chap. 1) Introduction 6 / 33
Organisation pratique
Organisation pratique36 heures de cours48 heures de séminaires (exercices avec assistant) et 24 h detravaux pratiques (exercices en salle machines)4 petits projets (programmes) + des devoirsTest écrit en milieu de quadrimestre (fin octobre - débutnovembre)Examen en janvier (interrogation dispensatoire), mai et août
Livres de référencesINFO-F-101, Syllabus du cours Programmation - Thierry Massart , PressesUniversitaires de Bruxelles (2011)DUPRÉ, XAVIER, Programmation avec le langage Python: un outil commode au servicede l’ingénieur, collection Technosup, Ellipses (2009), ISBN 978-2-7298-4387-8DOWNEY, A., Think Python: how to think like a computer scientist, Green Tea Press(2009)www.greenteapress.com/thinkpython/thinkpython.html
AHO, A. et ULLMAN, J., Concepts fondamentaux de l’Informatique, Dunod (1993)
Programmation (Chap. 1) Introduction 7 / 33
Organisation pratique
Organisation pratiqueEquipe
Thierry MASSART, Assistants: Liran LERMAN, Markus LINDSTRÖM,Luciano PORRETTA, Nikita VESHCHIKOV
Bureaux :
Thierry Massart: Campus Plaine, bâtiment NO, 8ème niveaubureau 2.N8.113
Markus (coordinateur des exercices): Campus Plaine bâtimentNO, 8ème niveau, bureau 2N8.211
Emails: [email protected]
Programmation (Chap. 1) Introduction 8 / 33
Algorithmes et programmes
Algorithmes et programmes
Programmation (Chap. 1) Introduction 9 / 33
Algorithmes et programmes
Qu’est-ce qu’un algorithme ?Tâche principale de l’informaticien : résoudre des problèmes
Problème Solution
Algorithme
1 spécification du problème� qu’a-t-on besoin pour résoudre le problème (entrées) ?� que doit-on fournir comme solution (sorties) ?
2 résolution : comment trouver la solution (méthode, algorithme) ?
DéfinitionUn algorithme est une séquence d’étapes précises et non ambiguëspouvant être exécutées de façon automatique.
Programmation (Chap. 1) Introduction 10 / 33
Algorithmes et programmes
Exemples d’algorithmes
Spécification du problème :Entrée : L’heure h (entier allant de 0 à 23)Sortie : Un message de bienvenue spécifique
Algorithme :1: si h ≤ 6 alors2: dire “Bonne nuit”3: sinon si h ≤ 18 alors4: dire “Bonjour”5: sinon6: dire “Bonsoir”7: fin si
Programmation (Chap. 1) Introduction 11 / 33
Algorithmes et programmes
Exemples d’algorithmes
Spécification du problème :Entrée : Un dictionnaire et un motSortie : La définition du mot recherché
Algorithme :1: lire le premier mot du dictionnaire2: tant que le mot lu n’est pas le mot recherché faire3: lire le mot suivant4: fin tant que5: lire la définition du mot recherché
Programmation (Chap. 1) Introduction 12 / 33
Algorithmes et programmes
Exemples d’algorithmes
De manière plus efficace :1: ouvrir le dictionnaire au milieu2: tant que la page courante ne contient pas le mot faire3: ouvrir le dictionnaire au milieu de la partie restante4: fin tant que5: rechercher la définition du mot dans la page courante
Programmation (Chap. 1) Introduction 13 / 33
Algorithmes et programmes
Exemples d’algorithmes
De manière plus précise et moins ambiguë :1: pageDebut ← 12: pageFin← numéro de la dernière page du dictionnaire3: pageCourante← (pageDebut +pageFin)/24: tant que pageCourante ne contient pas le mot faire5: si le mot se trouve avant pageCourante alors6: pageFin← pageCourante7: sinon8: pageDebut ← pageCourante9: fin si
10: pageCourante← pageDebut +(pageDebut +pageFin)/211: fin tant que12: rechercher la définition du mot dans pageCourante
Programmation (Chap. 1) Introduction 14 / 33
Algorithmes et programmes
Qu’est-ce qu’un programme ?DéfinitionUn programme est une séquence d’instructions qui spécifie commentréaliser un calcul ou une tâche. Ils sont décrits à l’aide de langages deprogrammation.
Remarque : le mot “programme” est utilisé aussi pour l’application qui “tourne” sur la machine
Beaucoup de langages différents mais les instructions permettent parexemple de :
réaliser des opérations mathématiques (par ex. addition,multiplication)obtenir des données au clavier, depuis un fichier, etc.afficher des données à l’écran ou écrire des données dans unfichierexécution conditionnelle : vérifier si certaines conditions sontrespectées et exécuter la séquence d’instructions appropriéeréaliser une tâche de manière répétitive
Programmation (Chap. 1) Introduction 15 / 33
Algorithmes et programmes
Langages de programmationOn distingue les langages
de haut niveau : compréhensible par l’humain (souvent langageformel avec des mots issus de l’anglais) : Python, Java, C, C++,PHP, Scheme, Pascal, etc.
de bas niveau : exécutables par un ordinateur (langages machineou assembleur)
Avantages d’un programme écrit dans un langage de haut niveau :
beaucoup plus facile à écrire et à (re)lire;
plus rapide à écrire;
plus court;portable
� peut être utilisé sur différents types d’ordinateurs avec peu ou pasde modifications;
� un programme de bas niveau n’est exécutable que sur un typebien particulier d’ordinateurs et doit être réécrit pour d’autres.
Programmation (Chap. 1) Introduction 16 / 33
Algorithmes et programmes
Langages de programmation
RemarqueLa grande majorité des programmes sont écrits en langages de hautniveau (sauf quelques applications spécialisées)
En pratiqueUn programme compréhensible par l’humain (haut niveau) est traduiten un programme compréhensible par la machine (bas niveau) demanière automatique (par un programme dédié à cette tâche)
2 types de programmes “traducteurs” :
compilateurs
interpréteurs
Programmation (Chap. 1) Introduction 17 / 33
Algorithmes et programmes
Compilateurs
DéfinitionUn compilateur est un programme qui lit un code source et le traduiten un exécutable, avant l’exécution du programme.
code source : programme écrit dans un langage de haut niveauspécifique
exécutable : code compilé qui peut être exécuté sans être traduità nouveau
application : exécution du programme (qui “tourne” sur lamachine)
Exécuteur
Code source Exécutable Application
Compilateur
Programmation (Chap. 1) Introduction 18 / 33
Algorithmes et programmes
CompilateursExempleProgramme écrit en C, compilé avec gcc et exécuté dans une console.
#include <stdio.h>#include <time.h>
int main() {time_t timestamp = time(NULL);struct tm * t = localtime(×tamp);int hour = t->tm_hour;
if (hour <= 6)printf("Bonne nuit. ");
else if (hour <= 18)printf("Bonjour. ");
elseprintf("Bonsoir");
printf("Il est %02uh %02umin %02usec.\n", t->tm_hour, t->tm_min, t->tm_sec);
return 0;}
Programmation (Chap. 1) Introduction 19 / 33
Algorithmes et programmes
CompilateursExempleProgramme écrit en Java, compilé avec javac et exécuté dans uneconsole (avec java).
import java.util.Calendar;
public class Greeter {private Calendar d;
public Greeter() {updateTime();
}
public void updateTime() {d = Calendar.getInstance();
}
% import java.util.Date;
% public class Greeter {% private Date d;
% public Greeter() {% updateTime();% }
% public void updateTime() {% d = new Date();% }
Programmation (Chap. 1) Introduction 20 / 33
Algorithmes et programmes
Compilateurspublic void sayHello() {updateTime();int hour = d.get(Calendar.HOUR);
if (hour <= 6)System.out.print("Bonne nuit. ");
else if (hour <= 18)System.out.print("Bonjour. ");
elseSystem.out.print("Bonsoir. ");
System.out.println("Il est " + d.get(Calendar.HOUR) + "h " + d.get(Calendar.MINUTE)+ "min " + d.get(Calendar.SECOND) + "sec");}
public static void main(String[] args) {
Greeter g = new Greeter();g.sayHello();
}}% public void sayHello() {% updateTime();% int hour = d.getHours();
% if (hour <= 6)% System.out.print("Bonne nuit. ");% else if (hour <= 18)% System.out.print("Bonjour. ");% else% System.out.print("Bonsoir. ");
% System.out.println("Il est " + d.getHours() + "h " + d.getMinutes()% + "min " + d.getSeconds() + "sec");% }
% public static void main(String[] args) {% Greeter g = new Greeter();% g.sayHello();% }% }
Programmation (Chap. 1) Introduction 21 / 33
Algorithmes et programmes
Interpréteurs
DéfinitionUn interpréteur est un programme qui lit un code source et l’exécutepas à pas.
Code source Application
Interpréteur
ExempleProgramme interprété par Python des 2 façons suivantes
mode interactif (dans une console spéciale appelée IDLE)
mode script (à partir d’un fichier)
Programmation (Chap. 1) Introduction 22 / 33
Algorithmes et programmes
Python : mode interactifLes caractères >>> indiquent que l’interpréteur est prêt à recevoir desinstructions (prompt ou invite de commande)
>>> from datetime import datetime>>> d = datetime.now()>>> hour = d.hour>>> print hour
10
>>> if hour <= 6:print ’Bonne nuit.’
elif hour <= 18:print ’Bonjour.’
else:print ’Bonsoir.’
Bonjour.
>>> print ’Il est’, d.hour, ’h’, d.minute, ’min’, d.second, ’sec’
Il est 10 h 35 min 4 sec
Programmation (Chap. 1) Introduction 23 / 33
Algorithmes et programmes
Python : mode scriptFichier say_hello.py
from datetime import datetime
d = datetime.now()hour = d.hour
if hour <= 6:print ’Bonne nuit.’
elif hour <= 18:print ’Bonjour. ’
else:print ’Bonsoir.’
print ’Il est’, d.hour, ’h’, d.minute, ’min’, d.second, ’sec’
A lancer avec la commande python say_hello.py
Bonjour.Il est 10 h 42 min 13 sec
Programmation (Chap. 1) Introduction 24 / 33
Algorithmes et programmes
Choix du langage pour le cours
Le langage utilisé dans ce cours pour implémenter lesalgorithmes sera le Python (version 2.7)
Durant les travaux pratiques : comment l’installer chez vous
Utilisation d’IDLE et de scripts
http://www.python.org (téléchargement, documentation, etc.)
Programmation (Chap. 1) Introduction 25 / 33
Erreurs (bugs)
Erreurs (bugs)
Programmation (Chap. 1) Introduction 26 / 33
Erreurs (bugs)
Erreurs (bugs)
Les erreurs sont fréquentes quand on programme.
Trois sortes d’erreurs :
erreurs de syntaxe
erreurs à l’exécution (ou exceptions)
erreurs sémantiques (ou logiques)
Deboguer (debug) : processus de repérage et correction des erreurs
Exemple de programme sans erreur :
>>> print ’La somme des deux premiers nombres entiers positifs est’, (1+2)
La somme des deux premiers nombres entiers positifs est 3
Programmation (Chap. 1) Introduction 27 / 33
Erreurs (bugs)
Erreurs de syntaxe
DéfinitionUne erreur de syntaxe est une violation des règles du langage deprogrammation. Celle-ci est signalée par le compilateur oul’interpréteur lors de son analyse (parsing).
Exemple (manque une parenthèse) :
>>> print ’La somme des deux premiers nombres entiers positifs est’, 1+2)
SyntaxError: invalid syntax
Exemple (manque une apostrophe) :
>>> print ’La somme des deux premiers nombres entiers positifs est, (1+2)
SyntaxError: EOL while scanning single-quoted string
Programmation (Chap. 1) Introduction 28 / 33
Erreurs (bugs)
Exceptions
DéfinitionUne exception (ou erreur à l’exécution) est une erreur qui apparaîtaprès le lancement du programme quand quelque chosed’exceptionnel se produit et qui n’est pas du à une violation de lasyntaxe.
Exemples : ouverture d’un fichier sur lequel l’utilisateur n’a pas lesdroits d’accès, perte de la connexion réseau, etc.
Programmation (Chap. 1) Introduction 29 / 33
Erreurs (bugs)
Erreurs sémantiquesDéfinitionUne erreur sémantique (ou erreur logique) se produit quand leprogramme ne donne pas le résultat attendu.
apparaît quand le programme est syntaxiquement correct mais lalogique des instructions est erronée
peut être difficile à identifier et corriger (travail de “détective” :savoir déboguer est une importante qualité du programmeur)
Exemples :
>>> print ’La somme des deux premiers nombres entiers positifs est’, (1+3)
La somme des deux premiers nombres entiers positifs est 4
>>> print ’La somme des trois premiers nombres entiers positifs est’, (1+2)
La somme des trois premiers nombres entiers positifs est 3
Programmation (Chap. 1) Introduction 30 / 33
Glossaire
Glossaire
Programmation (Chap. 1) Introduction 31 / 33
Glossaire
Glossairerésolution de problème : processus de formulation d’un problème (spécification), trouverune solution et exprimer la solution
algorithme : séquence d’étapes précises et non ambiguës pouvant être exécutées defaçon automatique
programme : séquence d’instructions écrites dans un langage de programmationparticulier et qui spécifie comment réaliser un calcul ou une tâche
langage de haut niveau : un langage comme Python facile à lire et écrire pour l’humain
langage de bas niveau : un langage exécutable par un ordinateur (aussi appelé langagemachine ou assembleur)
portabilité : le fait qu’un programme puisse être exécuté sur plus d’une sorte d’ordinateurs
interpréter : exécuter un programme écrit dans un langage de haut niveau en letraduisant pas à pas
compiler : traduire un programme écrit dans un langage de haut niveau en un langage debas niveau en une fois, en vue de sa future exécution
code source : un programme écrit en langage de haut niveau avant sa compilation ou soninterprétation
exécutable : un programme après compilation, prêt à être exécuté
prompt : les caractères >>> qui indiquent que l’interpréteur est prêt à recevoir desinstructions
Programmation (Chap. 1) Introduction 32 / 33
Glossaire
Glossaire
script : un programme stocké dans un fichier (en vue d’être interprété)
mode interactif : une manière d’utiliser Python en tapant les instructions via le prompt
mode script : une manière d’utiliser Python en lisant les instructions depuis un fichier
bug : une erreur dans un programme
deboguer : le processus d’identification et de correction des 3 types d’erreurs deprogrammation
syntaxe : la structure et les règles d’un langage de programmation
erreur de syntaxe : une violation des règles d’écriture dans un programme qui le rendimpossible à interpréter ou à compiler
exception : une erreur détectée pendant l’exécution du programme
sémantique : la signification (la logique, le sens) d’un programme
erreur sémantique : une erreur dans un programme qui fait que quelque chose dedifférent de ce que le programmeur voulait réaliser se produit
parser : examiner un programme et analyser sa structure syntaxique
print : instruction qui ordonne à l’interpréteur Python d’afficher une valeur à l’écran
Programmation (Chap. 1) Introduction 33 / 33
Chapitre 2. Types, variables et expressions
Contenu du chapitre
1 Valeurs et types
2 Variables
3 Opérateurs et expressions
4 Commentaires
5 Erreurs fréquentes
6 Glossaire
Programmation (Chap. 2) Types, variables et expressions 1 / 27
Valeurs et types
Valeurs et types
Programmation (Chap. 2) Types, variables et expressions 2 / 27
Valeurs et types
Valeurs et types
Une valeur est un des éléments de base d’un programme, comme un
nombre ou une lettre. Exemples : 1, 2 ou ’Bonjour’
Toute valeur possède un type particulier.
’Bonjour’ est une chaîne de caractères (string), identifiable (par
vous et l’interpréteur Python) grâce aux apostrophes : str
1 et 2 sont des entiers (integer) : int
1.4 et 2.0 sont des réels (nombres à virgule flottante,
floating-point) : float
Programmation (Chap. 2) Types, variables et expressions 3 / 27
Valeurs et types
Valeurs et types
L’interpréteur peut vous donner le type d’une valeur :
>>> type(4)<type ’int’>>>> type(’Bonjour’)<type ’str’>>>> type(1.2)<type ’float’>
Différences de types
le point détermine un float
les apostrophes déterminent un str
>>> type(2)<type ’int’>>>> type(2.)<type ’float’>>>> type(’2’)
<type ’str’>
Programmation (Chap. 2) Types, variables et expressions 4 / 27
Variables
Variables
Programmation (Chap. 2) Types, variables et expressions 5 / 27
Variables
Variables
Une variable est un nom (symbolique) qui permet de faire référence à
une valeur (et de la stocker en mémoire).
Une assignation est une instruction qui permet de créer une nouvelle
variable et de lui attribuer une valeur. Elle permet également de mettre
à jour la valeur d’une variable déjà créée.
>>> i = 12>>> message = ’La valeur de PI est’>>> pi = 3.14159265>>> print i12>>> print message, piLa valeur de PI est 3.14159265>>> i = 5>>> print i
5
Programmation (Chap. 2) Types, variables et expressions 6 / 27
Variables
Diagramme d’état
On peut représenter les variables par un diagramme d’état.
message
pi
i
’La valeur de PI est’
12
3.14159265
Le type d’une variable est le type de la valeur qu’elle réfère.
>>> type(i)<type ’int’>>>> type(message)<type ’str’>>>> type(pi)<type ’float’>
Programmation (Chap. 2) Types, variables et expressions 7 / 27
Variables
Exemple d’utilisation de variables
Les variables permettent
d’associer un nom à une valeur
de réutiliser une valeur (la conserver)
>>> rayon = 10>>> circonference = 2 * pi * rayon>>> aire = pi * rayon * rayon>>> print circonference
62.831853
>>> print aire
314.59265
Remarque : le symbole * permet la multiplication
Programmation (Chap. 2) Types, variables et expressions 8 / 27
Variables
Modification de la valeur d’une variable
On peut réaffecter une valeur à une variable.
>>> rayon = 10>>> circonference = 2 * pi * rayon>>> print circonference62.831853
>>> rayon = 2>>> print rayon2
>>> print circonference62.831853
>>> circonference = 2 * pi * rayon>>> print circonference12.5663706
rayon 10
circonference 62.831853
12.5663706
rayon 2
circonference 62.831853
rayon 2
circonference
pi
pi
3.14159265
pi 3.14159265
3.14159265
Programmation (Chap. 2) Types, variables et expressions 9 / 27
Variables
Votre premier algorithme
Problème
Vous avez deux variables a et b en entrée. Comment faire pour
échanger leurs valeurs ?
>>> a = 17>>> b = 21>>> ???>>> print a
21
>>> print b17
Programmation (Chap. 2) Types, variables et expressions 10 / 27
Variables
Votre premier algorithme
Solution : utiliser une troisième variable
>>> a = 17>>> b = 21
>>> a_old = a
>>> a = b
>>> b = a_old>>> print a21
>>> print b17
17
21
a
b
21
21
a_old 17
a
b
21
17
a_old 17
a
b
17
21
17a_old
a
b
Programmation (Chap. 2) Types, variables et expressions 11 / 27
Variables
Noms des variables
Le nom d’une variable
Contraintes :
� ne peut contenir que des lettres (minuscules et majuscules), des
chiffres et le caractère _ (underscore)
� doit commencer par une lettre ou le caractère _
Conventions :
� devrait avoir du sens (dire ce qu’elle représente)
� devrait commencer par une lettre minuscule
Attention à la casse : pi est différent de Pi ou de PI
Exemples de noms valides : x1, maVariable, mon_entier
Exemples de noms invalides : 2be, x@, print
Pourquoi print n’est-il pas valide ?
Programmation (Chap. 2) Types, variables et expressions 12 / 27
Variables
Mots-clef
On ne peut utiliser print pour une variable car c’est un mot-clef (mot
réservé) du langage Python
Python (2.6) a 31 mots-clef :
and del from not whileas elif global or withassert else if pass yieldbreak except import printclass exec in raisecontinue finally is returndef for lambda try
Si l’interpréteur se plaint du nom d’une de vos variables, consultez
cette liste !
Programmation (Chap. 2) Types, variables et expressions 13 / 27
Opérateurs et expressions
Opérateurs et expressions
Programmation (Chap. 2) Types, variables et expressions 14 / 27
Opérateurs et expressions
Opérateurs
Les opérateurs sont des symboles spéciaux qui représentent des
opérateurs arithmétiques comme l’addition ou la multiplication. Les
opérandes peuvent être (entre autres) des valeurs ou des variables de
type int ou float.
+→ addition (20 + 32)
-→ soustration (hour - 1)
*→ multiplication (hour * 60 + minute)
/→ division (minute / 60)
**→ exposant (5 ** 2)
%→ modulo (7 % 2) : reste de la division entière
Les parenthèses peuvent également être utilisées :
(5 + 9) * (15 - 7)
Programmation (Chap. 2) Types, variables et expressions 15 / 27
Opérateurs et expressions
Division entière
Si les deux opérandes sont de type int, l’opérateur de division / ne
fait pas ce qu’on pourrait attendre.
>>> minute = 59>>> minute / 60
0
On s’attend à obtenir 0.983333 mais on obtient 0.
C’est la division entière : l’interpréteur retourne une valeur de type intqui est la partie entière du résultat.
Note : ce comportement est très pratique dans certains cas (en
conjonction avec % : voir plus tard)
Programmation (Chap. 2) Types, variables et expressions 16 / 27
Opérateurs et expressions
Division entière
Solution : au moins une des deux opérandes doit être de type float
>>> minute / 60.0
0.98333333333333328
Remarque : on constate une erreur d’approximation. Cela peut arriver
avec des nombres de type float. Cela est dû à la manière dont ces
nombres sont représentés sur la machine (on reviendra sur ce
problème plus tard).
L’erreur d’approximation affichée peut varier en fonction de la machine
et de la configuration de Python.
Programmation (Chap. 2) Types, variables et expressions 17 / 27
Opérateurs et expressions
Expressions
Une expression est une combinaison de valeurs, variables et
opérateurs.
Exemples :
17
x
x + 17
En mode interactif, l’interpréteur évalue l’expression et affiche le
résultat.
>>> 1 + 1
2
Par contre, dans un script, une expression est évaluée mais le résultat
n’est pas affiché : on peut croire qu’il ne se passe rien si on ne
demande pas d’afficher le résultat !
Programmation (Chap. 2) Types, variables et expressions 18 / 27
Opérateurs et expressions
Règles de précédence
L’ordre de précédence des opérateurs est le même qu’en
mathématiques :
1 parenthèses (donc 2 * (3 - 1) donne 4)
2 exposant (donc 2 ** 1 + 1 donne 3 et 3 * 1 ** 3 donne 3 et
pas 27)
3 multiplication et division (donc 2 * 3 - 1 donne 5)
4 addition et soustraction
L’ordre d’évaluation des opérateurs ayant la même précédence est
celui de l’arithmétique classique (ex: addition, soustraction,
multiplication, division, de gauche à droite, exponentiation de droite à
gauche).
Exemple : d / 2 * pi : la division est effectuée en premier, et le
résultat est doncd ·π2
si d est un réel (et si d est un entier ?) (Pour
obtenird2π , il faut écrire d / (2 * pi))
2**3**2 vaut 512 (2**(3**2))
Programmation (Chap. 2) Types, variables et expressions 19 / 27
Opérateurs et expressions
Opérations sur les chaînes de caractères
On ne peut pas utiliser tous les opérateurs mathématiques sur des
opérandes de type str.
Opérateurs acceptés pour les chaînes
l’opérateur + permet de concaténer deux str (attacher le
deuxième str au premier)
>>> first = ’chat’>>> second = ’eau’>>> together = first + second>>> print togetherchateau
l’opérateur * utilisé sur un str et un int permet de répéter le str>>> ’ha’ * 3hahaha
Programmation (Chap. 2) Types, variables et expressions 20 / 27
Commentaires
Commentaires
Programmation (Chap. 2) Types, variables et expressions 21 / 27
Commentaires
Commentaires
Pour être plus simple à lire, un code source devrait être correctement
documenté.
On peut ajouter des notes dans du code, qui ne seront pas
interprétées. On appelle cela des commentaires.
Une manière de faire en Python : tout ce qui suit le symbole # est
ignoré jusqu’à la fin de la ligne.
# calcule le pourcentage de l’heure écouléepercent = (minute * 100) / 60
v = 5 # assigne 5 à v
v = 5 # vitesse en metres / secondes
Quels commentaires trouvez-vous utiles et pourquoi ?
Programmation (Chap. 2) Types, variables et expressions 22 / 27
Erreurs fréquentes
Erreurs fréquentes
Programmation (Chap. 2) Types, variables et expressions 23 / 27
Erreurs fréquentes
Erreurs fréquentes
utiliser un mot-clef comme nom de variable (par ex. class)
mettre un espace dans le nom d’une variable (par ex. bad name)
utiliser une variable avant de l’avoir créée (pour créer une
variable: la placer à gauche d’une assignation)
>>> principal = 257.50>>> interet = principal * taux
Traceback (most recent call last):File "<pyshell#88>", line 1, in <module>interet = principal * taux
NameError: name ’taux’ is not defined
ne pas respecter la “casse” de caractères : par ex., pi est
différent de Pi
erreur sémantique sur l’ordre des opérations :
par ex., écrire 1.0 / 2.0 * pi pour1
2π
Programmation (Chap. 2) Types, variables et expressions 24 / 27
Glossaire
Glossaire
Programmation (Chap. 2) Types, variables et expressions 25 / 27
Glossaire
Glossaire
valeur : unité élémentaire de données, comme un nombre ou une chaîne de caractères,
qu’un programme manipule.
type : catégorie de valeurs. Les types vus jusqu’à présent : entiers (int), réels (float),
et chaînes de caractères (str).
variable : nom qui réfère à une valeur.
diagramme d’état : représentation graphique d’un ensemble de variables et des valeurs
qu’elles réfèrent.
instruction : morceau du code qui représente une commande ou une action. Jusqu’à
présent les instructions vues sont les assignations et les instructions d’affichage (print).
assignation : instruction qui assigne une valeur à une variable (variable = valeur)
opérateur : symbole spécial qui représente une opération simple comme l’addition, la
multiplication ou la concaténation de chaînes de caractères.
opérande : une des valeurs sur lesquelles un opérateur s’applique.
division entière : opération qui divise deux nombres entiers et retourne un entier
(uniquement la partie entière du résultat).
expression : combinaison de variables, d’opérateurs et de valeurs et dont le résultat est
une valeur.
Programmation (Chap. 2) Types, variables et expressions 26 / 27
Glossaire
Glossaire
évaluer : simplifier une expression en appliquant les opérations dans le but d’obtenir la
valeur résultat.
règles de précédence : ensemble de règles définissant l’ordre dans lequel les
expressions impliquant plusieurs opérateurs et opérandes sont évaluées.
concaténer : joindre bout à bout des chaînes de caractères.
commentaire : information dans un programme destinée au lecteur du code source et qui
n’a pas d’effet sur l’exécution du programme.
mot-clef : mot reservé et utilisé par le langage Python pour parser un programme; on ne
peut pas utiliser les mots-clef comme nom de variables.
Programmation (Chap. 2) Types, variables et expressions 27 / 27
Chapitre 3. Fonctions
Contenu du chapitre
1 Appels de fonctions
2 Définir de nouvelles fonctions
3 Flot d’exécution
4 Documenter ses fonctions
5 Paramètres par défaut et arguments mots-clefs
6 Erreurs fréquentes
7 Glossaire
Programmation (Chap. 3) Fonctions 1 / 49
Appels de fonctions
Appels de fonctions
Programmation (Chap. 3) Fonctions 2 / 49
Appels de fonctions
Faire appel à une fonction
Définition
Une fonction est une séquence d’instructions à laquelle on donne un
nom. Elle peut prendre en entrée une liste d’arguments et fournit en
sortie une valeur de retour.
On peut voir une fonction comme une boîte
noire qui effectue un travail.
les arguments sont ce qu’elle a besoin
pour faire son travail (entrées)
la valeur de retour est le résultat de son
travail
Programmation (Chap. 3) Fonctions 3 / 49
Appels de fonctions
Faire appel à une fonction
on reconnaît l’appel à une fonction par l’utilisation de parenthèses
qui entourent les arguments
on dit qu’une fonction “prend” quelque chose comme argument et
“retourne” un résultat
Exemple : la fonction int convertit une chaîne en entier
>>> int(’32’)
32>>> s = ’100’ + ’20’>>> x = int(s)>>> print x
10020
32’32’ int
Programmation (Chap. 3) Fonctions 4 / 49
Appels de fonctions
Fonctions de conversion de types
Python fournit une série de fonctions permettant de convertir le type
d’une valeur.
int() : chaîne de caractères→ entier : ok si la chaîne
représente un entier
>>> int(’32’)
32
>>> int(’Bonjour’)
Traceback (most recent call last):File "<pyshell#94>", line 1, in <module>int (’Bonjour’)
ValueError: invalid literal for int() with base 10: ’Bonjour’
int() : réel→ entier : ne garde que la partie entière
>>> int(3.99999)
3
>>> int(-2.3)
-2
Programmation (Chap. 3) Fonctions 5 / 49
Appels de fonctions
Fonctions de conversion de types
float() : entier ou chaîne→ réel : ok si la chaîne représente
un réel
>>> float(32)
32.0
>>> float(’3.14159’)
3.14159
str() : entier ou réel→ chaîne
>>> str(32)
’32’
>>> str(3.14159)
’3.14159’
Programmation (Chap. 3) Fonctions 6 / 49
Appels de fonctions
Modules
Python possède une très grande librairie de fonctions. Certaines ne
sont pas accessibles directement, il faut les importer.
Définition
Un module est un fichier qui contient une collection de fonctions et
variables reliées (ainsi que d’autres choses, voir plus tard).
Exemple :
>>> import math>>> print math
<module ’math’ from ’/usr/lib/python2.6/lib-dynload/math.so’>
importation du module math
math est un objet module (valeur qui donne accès aux fonctions,
variables, etc. définies dans le module)
Programmation (Chap. 3) Fonctions 7 / 49
Appels de fonctions
Modules et fonctions
Pour accéder aux fonctions (appel de fonction) ou aux variables
définies dans un module, on utilise la notation “point”:
nom_module.nom_fonction(arguments)nom_module.nom_variable
Exemple :
>>> radians = 0.7>>> height = math.sin(radians)>>> print height0.64421768723769102
>>> print math.pi
3.14159265359
On peut assigner la valeur de retour d’une fonction à une variable.
Programmation (Chap. 3) Fonctions 8 / 49
Appels de fonctions
Modules et fonctions
Les fonctions sin, cos, tan, etc. prennent des arguments exprimés
en radians.
Conversion de degrés en radians : diviser par 360 et multiplier par 2π>>> degrees = 45>>> radians = degrees / 360.0 * 2 * math.pi>>> math.sin(radians)
0.707106781186547
>>> math.sqrt(2) / 2.0
0.707106781186547
En mode interactif, une instruction qui est simplement un appel à
une fonction affiche son résultat
Pourquoi avoir écrit 360.0 ? Comment aurions-nous pu faire
autrement ?
Programmation (Chap. 3) Fonctions 9 / 49
Appels de fonctions
Composition
Caractéristique très utile des langages de programmation: on peut
composer (combiner) leurs éléments (variables, expression,
instructions, etc.)
Exemple : un argument d’une fonction peut être une expression ou un
appel à une fonction.
x = math.sin(degrees / 360.0 * 2 * math.pi)x = math.exp(math.log(x+1))
une expression ou une fonction peut être utilisée comme une valeur
la valeur (ou le type) d’une expression ou d’une fonction est son résultat
(ou le type de son résultat)
exception : le membre gauche d’une assignation doit toujours être le
nom d’une variable (sinon erreur de syntaxe)1
minutes = hours * 60 # OKhours * 60 = minutes # SyntaxError!
1Il y a cependant des exceptions à cette règle, voir plus tard.
Programmation (Chap. 3) Fonctions 10 / 49
Appels de fonctions
Documentation sur les modules
Comment connaître les fonctions d’un module et leurs arguments ?
Option 1 : Consultez la documentation sur www.python.org.
La recherche de “math” dans l’onglet “search” donne comme premier lien:
Programmation (Chap. 3) Fonctions 11 / 49
Appels de fonctions
Documentation sur les modules
Comment connaître les fonctions d’un module et leurs arguments ?
Option 2 : Utilisez help() et dir() dans IDLE
>>> import math>>> help(math)(longue aide: voir IDLE)>>> dir(math)
[’__doc__’, ’__file__’, ’__name__’, ’acos’, ’asin’, ’atan’, ’atan2’,
’ceil’, ’cos’, ’cosh’, ’degrees’, ’e’, ’exp’, ’fabs’, ’floor’, ’fmod’,
’frexp’, ’hypot’, ’ldexp’, ’log’, ’log10’, ’modf’, ’pi’, ’pow’, ’radians’,
’sin’, ’sinh’, ’sqrt’, ’tan’, ’tanh’]
Programmation (Chap. 3) Fonctions 12 / 49
Appels de fonctions
Documentation sur les modules
>>> help(math.degrees)
Help on built-in function degrees in module math:degrees(...)
degrees(x) -> converts angle x from radians to degrees>>> help(math.sin)Help on built-in function sin in module math:sin(...)
sin(x) -> Return the sine of x (measured in radians).
Programmation (Chap. 3) Fonctions 13 / 49
Appels de fonctions
Fonctions silencieuses et bruyantes
Les fonctions comme math.sin ou int sont des fonctions
“silencieuses” :
en mode script, toujours silencieuses
en mode interactif, si on assigne la valeur de retour, rien n’est
affiché
en mode interactif, un appel seul affiche la valeur de retour, mais
c’est uniquement pour rendre les choses plus faciles
vision intuitive : boîte noire
>>> result = int(’32’)>>> print result
32
32’32’ int
Programmation (Chap. 3) Fonctions 14 / 49
Appels de fonctions
Fonctions silencieuses et bruyantes
Certaines fonctions font du “bruit” :
leur appel provoque un affichage, une écriture dans un fichier,
etc.
le comportement de la fonction est visible en dehors de sa seule
valeur de retour
vision intuitive : boîte noire avec “écran d’affichage”
>>> result = help(math.sin)Help on built-in function sinin module math:sin(...)
sin(x) -> Return the sineof x (measured in radians).>>> print result
None
math.sin help
Remarque : None est une valeur spéciale (de type NoneType) utilisée quand
une fonction ne retourne “rien”
Programmation (Chap. 3) Fonctions 15 / 49
Appels de fonctions
Fonctions silencieuses et bruyantes
En général,
la plupart des fonctions sont silencieuses (car réutilisables : on
contrôle ce qui est affiché ou pas)
sauf celles dont le rôle est explicitement d’afficher quelque chose
ou de modifier un fichier, etc.
Programmation (Chap. 3) Fonctions 16 / 49
Définir de nouvelles fonctions
Définir de nouvelles fonctions
Programmation (Chap. 3) Fonctions 17 / 49
Définir de nouvelles fonctions
Pourquoi créer des fonctions ?
Diviser un programme en fonctions permet de
rassembler et donner un nom à un groupe d’instructions :
programme plus facile à lire et à déboguer
éliminer du code répétitif : programmes plus courts, plus faciles
(et moins dangereux) à modifier
diviser des tâches importantes en petits morceaux : permet de
déboguer chaque morceau indépendamment
réutiliser du code : des fonctions bien conçues (et souvent
silencieuses) sont souvent utiles pour d’autres programmes
Programmation (Chap. 3) Fonctions 18 / 49
Définir de nouvelles fonctions
Définition de nouvelles fonctions
Comment créer ses propres fonctions ?
Définition
Une définition de fonction spécifie le nom, les arguments (optionnel) et
la séquence d’instructions de la fonction.
def print_lyrics():print ’Frere Jacques, Frere Jacques’print ’Dormez-vous ? Dormez-vous ?’
def get_sum(x, y):return x + y
Une définition de fonction comporte deux éléments: l’en-tête et le
corps.
Programmation (Chap. 3) Fonctions 19 / 49
Définir de nouvelles fonctions
Définition de nouvelles fonctions: en-tête
def print_lyrics():print ’Frere Jacques, Frere Jacques’print ’Dormez-vous ? Dormez-vous ?’
def get_sum(x, y):return x + y
Spécification de l’en-tête de la fonction :
def est un mot-clef qui indique qu’il s’agit d’une définition, il est
suivi par le nom de la fonction
la liste des noms des arguments est donnée entre parenthèses
les parenthèses sont obligatoires (même s’il n’y a pas
d’arguments)
la liste des arguments est suivi du caractère : (deux points)
les noms des fonctions suivent les mêmes règles que les noms
des variables (cf. Chap. 2)
Programmation (Chap. 3) Fonctions 20 / 49
Définir de nouvelles fonctions
Définition de nouvelles fonctions : corps
def print_lyrics():print ’Frere Jacques, Frere Jacques’print ’Dormez-vous ? Dormez-vous ?’
def get_sum(x, y):return x + y
Spécification du corps de la fonction :
liste d’instructions qui peuvent utiliser les arguments (comme des
variables)
ces instructions doivent être indentées
l’indentation sert à grouper les instructions (spécifier ce qui fait
partie du corps) : fait partie de la syntaxe (obligatoire)
en pratique, l’indentation est un nombre constant de caractères
“espace” en début de chaque ligne (convention : 4 espaces)
évitez les tabulations pour indenter (problèmes entre éditeurs)
nombre d’instructions non limité
on utilise le mot-clef return pour retourner une valeur
Programmation (Chap. 3) Fonctions 21 / 49
Définir de nouvelles fonctions
Définition de nouvelles fonctions
En mode interactif,
indentation automatique
ligne blanche pour terminer la définition d’une fonction
>>> def get_sum(x, y):return x + y
>>>
Programmation (Chap. 3) Fonctions 22 / 49
Définir de nouvelles fonctions
Appel de ses propres fonctions
Une fois définie, une fonction personnelle s’appelle de la même façon
que les fonctions prédéfinies.
>>> print_lyrics()Frere Jacques, Frere JacquesDormez-vous ? Dormez-vous ?>>> get_sum(4, 12)16>>> get_sum(math.pi, math.e)5.85987448205>>> get_sum(’Bon’, ’jour’)Bonjour>>> def repeat_lyrics():
print_lyrics()print_lyrics()
>>> repeat_lyrics()Frere Jacques, Frere JacquesDormez-vous ? Dormez-vous ?Frere Jacques, Frere JacquesDormez-vous ? Dormez-vous ?
1612
4x
y get_sum
Programmation (Chap. 3) Fonctions 23 / 49
Définir de nouvelles fonctions
Ordre des arguments
L’ordre des arguments est important !
>>> def print_ratio(x, y):print float(x) / y
>>> print_ratio(12, 4)3.0>>> print_ratio(4, 12)
0.333333333333
y12
4
x
print_ratio
3.0
y4
12
x
print_ratio
0.333333
Programmation (Chap. 3) Fonctions 24 / 49
Définir de nouvelles fonctions
Paramètres
A l’intérieur d’une fonction, les arguments sont appelés des paramètres.
Exemple
def double_print(bruce):print bruceprint bruce
la fonction ci-dessus assigne son argument à un paramètre nommé
bruce, par ex. double_print(17) assigne la valeur 17 au paramètre
bruce lors de l’exécution de la fonction
la fonction marche avec n’importe quel type de paramètre sur lequel les
instructions sont valides, ici, simplement pouvoir être imprimé :
double_print(math.pi), double_print(’Hello’),
double_print(’ha’ * 3), double_print(math.cos(math.pi))
un argument est évalué avant l’appel de la fonction
une variable peut être utilisée comme argument : le nom de la variable
(william) n’a rien à voir avec le nom du paramètre (bruce)
>>> william = ’Etre ou ne pas etre’>>> double_print(william)
Programmation (Chap. 3) Fonctions 25 / 49
Définir de nouvelles fonctions
Variables locales
Un paramètre est une variable locale à sa fonction, c’est à dire qu’il
n’existe pas en dehors de sa fonction (en dehors de double_print,
bruce n’existe pas)
>>> def double_print(bruce):print bruceprint bruce
>>> double_print(’Hello’)
HelloHello
>>> print bruce
NameError: name ’bruce’ is not defined
Intuition : boîte noire (on ne voit pas le corps de la fonction de
l’extérieur de celle-ci)
Programmation (Chap. 3) Fonctions 26 / 49
Définir de nouvelles fonctions
Variables locales
De la même manière, toute variable définie à l’intérieur de la fonction
est une variable locale. Elle est détruite quand l’appel à la fonction est
terminé.
>>> def double_cat(part1, part2):cat = part1 + part2double_print(cat)
>>> line1 = ’Big ’>>> line2 = ’Bang.’>>> double_cat(line1, line2)
Big Bang.Big Bang.
>>> print cat
NameError: name ’cat’ is not defined
On parle de la portée des variables : zone du programme dans
laquelle elle est disponible.
Programmation (Chap. 3) Fonctions 27 / 49
Définir de nouvelles fonctions
Pile de fonctions
Le flot d’exécution de fonctions successives (pile de fonctions) et les
variables peuvent être représentés par un diagramme de pile.
chaque fonction est représentée
dans un cadre
les cadres sont ordonnés dans
une pile qui indique quelle
fonction en appelle une autre
les variables définies dans la
zone du programme constituée
des instructions ne se trouvant
pas dans une fonction sont
représentées dans le premier
cadre
double_cat
double_print
’Big ’
part1 ’Big ’
part2 ’Bang.’
cat
line1
’Bang.’
’Big Bang.’
bruce ’Big Bang.’
line2
Programmation (Chap. 3) Fonctions 28 / 49
Définir de nouvelles fonctions
Pile de fonctions
Si une exception se produit pendant l’appel à une fonction, Python
affiche la pile des fonctions en cours.
Par exemple, si on ajoute print cat dans double_print:
Traceback (most recent call last):File "stack.py", line 12, in <module>
double_cat(line1,line2)File "stack.py", line 8, in double_cat
double_print(cat)File "stack.py", line 4, in double_print
print catNameError: global name ’cat’ is not defined
Programmation (Chap. 3) Fonctions 29 / 49
Flot d’exécution
Flot d’exécution
Programmation (Chap. 3) Fonctions 30 / 49
Flot d’exécution
Flot d’exécution
une fonction doit être définie (ou importée depuis un module) avant
d’être appelée
l’ordre dans lequel des instructions sont exécutées est appelé le flot
d’exécution
l’exécution commence toujours à la première instruction
les instructions suivantes sont ensuite exécutées de haut en bas, une
après l’autre
les définitions de fonctions n’altère pas le flot d’exécution (elles sont
lues et deviennent disponibles) mais le corps d’une fonction n’est
exécuté que quand une fonction est appelée
un appel à une fonction peut être vu comme un détour dans le
programme
le flot d’exécution n’est donc pas linéaire
Programmation (Chap. 3) Fonctions 31 / 49
Flot d’exécution
Flot d’exécution
Exemple:
l’ordre des définitions est-il valide ?
si oui, pouvez-vous décrire le flot d’exécution du script suivant ?
(à l’aide de la numérotation)
def print_jacques():print get_twice(’Frere Jacques’) #1
def get_twice(s):return s + ’, ’ + s #2
def print_song():print_jacques() #3print_dormez() #4
def print_dormez():print get_twice(’Dormez-vous ?’) #5
print_song() #6
6→ 3→ 1→ 2→4→ 5→ 2
Exercice : dessiner
l’évolution de la pile
des fonctions à
chaque étape.
Programmation (Chap. 3) Fonctions 32 / 49
Flot d’exécution
Flot d’exécution
Exemple:
l’ordre des définitions est-il valide ?
si oui, pouvez-vous décrire le flot d’exécution du script suivant ?
(à l’aide de la numérotation)
def print_jacques():print get_twice(’Frere Jacques’) #1
def get_twice(s):return s + ’, ’ + s #2
def print_song():print_jacques() #3print_dormez() #4
def print_dormez():print get_twice(’Dormez-vous ?’) #5
print_song() #6
6→ 3→ 1→ 2→4→ 5→ 2
Exercice : dessiner
l’évolution de la pile
des fonctions à
chaque étape.
Programmation (Chap. 3) Fonctions 32 / 49
Flot d’exécution
Flot d’exécution
Une instruction return interrompt l’exécution de la fonction.
>>> def fonc():print ’avant’return 1print ’apres’
>>> x = fonc()
avant>>> print x
1
L’instruction print ’apres’ n’est jamais exécutée
Programmation (Chap. 3) Fonctions 33 / 49
Documenter ses fonctions
Documenter ses fonctions
Programmation (Chap. 3) Fonctions 34 / 49
Documenter ses fonctions
Documenter ses fonctions
On a vu qu’on peut obtenir de l’aide avec help.
>>> help(math.sin)Help on built-in function sin in module math:sin(...)
sin(x) -> Return the sine of x (measured in radians).
Qu’en est-il de nos fonctions ?
>>> help(get_sum)Help on function get_sum in module __main__:
get_sum(x, y)
Programmation (Chap. 3) Fonctions 35 / 49
Documenter ses fonctions
Documenter ses fonctions
Un commentaire multiligne commençe par """ et se termine par """.
""" this program makessome useful computationand is much commented."""x = 2 * 3y = x + 3 # we have already seen this type of comments
Différence entre les commentaires multiligne et les commentaires “fin
de ligne” : l’interpréteur ne les ignore pas toujours car ils sont utilisés
pour documenter certaines parties du programme (en dehors du seul
code).
Programmation (Chap. 3) Fonctions 36 / 49
Documenter ses fonctions
Documenter ses fonctions
Un docstring est commentaire multiligne placé au début du corps
d’une fonction.
>>> def get_sum(x, y):""" return the sum of x and y """return x + y
>>> help(get_sum)
Help on function get_sum in module __main__:get_sum(x, y)
return the sum of x and y
Documenter clairement ses fonctions : très bonne habitude à prendre !
Seules les instructions du corps sont cachées, toute l’information utile à
l’utilisation d’une fonction est accessible.
Formatez vos commentaires multiligne pour qu’ils soient facilement
lisible (par ex., max 79 caractères par ligne)
Programmation (Chap. 3) Fonctions 37 / 49
Documenter ses fonctions
Introspection
Les docstring montrent une des formes de l’introspection : particularité
du langage Python qui lui permet de s’auto-explorer.
>>> print get_sum.__doc__
return the sum of x and y>>> print get_sum.__name__get_sum>>> print math.sqrt.__module__math>>> print get_sum.__module__
__main__
Le module __main__ est le module par défaut.
Programmation (Chap. 3) Fonctions 38 / 49
Documenter ses fonctions
Objets et introspection
Définir une fonction crée une variable avec le même nom, dont la
valeur est un objet fonction (de type function)
>>> print get_sum
<function get_sum at 0x53ee30>>>> print type(get_sum)
<type ’function’>
→ c’est sur (grâce à) cet objet fonction que nous pouvons utiliser
l’introspection.
Programmation (Chap. 3) Fonctions 39 / 49
Documenter ses fonctions
Objets et introspection
On utilise la notation point pour accéder aux attributs d’un objet
fonction ou d’un objet module. La liste des attributs d’un objet est
disponible via dir.
>>> print get_sum.__doc__
return the sum of x and y>>> dir(get_sum)
[’__call__’, ’__class__’, ’__closure__’, ’__code__’, ’__defaults__’,
’__delattr__’, ’__dict__’, ’__doc__’, ’__format__’, ’__get__’,
’__getattribute__’, ’__globals__’, ’__hash__’, ’__init__’, ’__module__’,
’__name__’, ’__new__’, ’__reduce__’, ’__reduce_ex__’, ’__repr__’,
’__setattr__’, ’__sizeof__’, ’__str__’, ’__subclasshook__’, ’func_closure’,
’func_code’, ’func_defaults’, ’func_dict’, ’func_doc’, ’func_globals’,
’func_name’]
Programmation (Chap. 3) Fonctions 40 / 49
Documenter ses fonctions
Objets et introspection
Intuition de la notation point : on précise ce qu’on veut obtenir du plus
général au plus détaillé.
En français, on dirait plutôt l’inverse : “Afficher l’attribut __doc__ de la
fonction sin du module math”
>>> print math.sin.__doc__
sin(x)
Return the sine of x (measured in radians)
Programmation (Chap. 3) Fonctions 41 / 49
Paramètres par défaut et arguments mots-clefs
Paramètres par défaut et
arguments mots-clefs
Programmation (Chap. 3) Fonctions 42 / 49
Paramètres par défaut et arguments mots-clefs
Paramètres par défaut
Un paramètre par défaut est une valeur par défaut que l’on assigne à
un paramètre lors de la définition d’une fonction. Cela permet de
rendre un paramètre optionnel.
>>> def foo(n,limite,t=1):print t
>>> foo(7,100,2)2>>> foo(7,100)1
Les paramètres optionels doivent se trouver après les paramètres
obligatoires.
Programmation (Chap. 3) Fonctions 43 / 49
Paramètres par défaut et arguments mots-clefs
Arguments “mots-clefs”
Un argument mot-clef est un argument nommé lors d’un appel à une
fonction. Cela permet de modifier l’ordre des arguments, ce qui peut
être utile avec des arguments optionnels.
>>> def f(x,y=2,z=3):print ’x = ’ + str(x) + ’, y = ’ + str(y) + ’, z = ’ + str(z)
>>> f(6,5,7)x = 6, y = 5, z = 7>>> f(7)x = 7, y = 2, z = 3>>> f(9,10)x = 9, y = 10, z = 3>>> f(7,z=8)x = 7, y = 2, z = 8>>> f(z=1,y=2,x=3)x = 3, y = 2, z = 1>>> f(z=1,y=2)TypeError: f() takes at least 1 non-keyword argument (0 given)
Programmation (Chap. 3) Fonctions 44 / 49
Erreurs fréquentes
Erreurs fréquentes
Programmation (Chap. 3) Fonctions 45 / 49
Erreurs fréquentes
Erreurs fréquentes
Indentation : problèmes quand des espaces et des tabulations sont utilisées (utilisez des
espaces exclusivement)
Sauvegarde : n’oubliez pas de sauver votre script avant de l’exécuter, sinon vous ne
comprendrez pas pourquoi votre programme ne fonctionne pas (même s’il est correct)
Instruction return : l’instruction return termine l’exécution de la fonction, la suite n’est
pas exécutée
Programmation (Chap. 3) Fonctions 46 / 49
Glossaire
Glossaire
Programmation (Chap. 3) Fonctions 47 / 49
Glossaire
Glossaire
fonction : séquence d’instructions qui possède un nom. Les fonctions peuvent prendre
des arguments (ou pas) et peuvent retourner une valeur (ou pas : retourne None).
définition de fonction : instruction qui créée une nouvelle fonction, spécifie son nom, ses
paramètres, et les instructions qu’elle doit exécuter.
en-tête : (header) la première ligne de la définition d’une fonction (def, nom de la
fonction, liste d’arguments, caractère "deux points").
corps : (body) séquence d’instructions dans une définition de fonction.
paramètre : variable utilisée à l’intérieur d’une fonction qui réfère à la valeur passée en
argument.
appel de fonction : instruction qui exécute une fonction. Elle consiste en le nom de la
fonction suivi par une liste d’arguments entre parenthèses.
argument : valeur fournie à une fonction quand une fonction est appelée. Cette valeur est
assignée au paramètre correspondant dans le corps fonction.
variable locale : variable définie à l’intérieur d’une fonction. Une variable locale ne peut
être utilisée qu’à l’intérieur de sa fonction.
portée : la portée d’une variable est la zone du programme dans laquelle elle est
disponible. La portée d’une variable locale ou d’un paramètre est limitée à sa fonction.
valeur de retour : le résultat d’une fonction. Si un appel de fonction est utilisé comme une
expression, sa valeur de retour est la valeur de l’expression.
Programmation (Chap. 3) Fonctions 48 / 49
Glossaire
Glossaire
module : fichier qui contient une collections de fonctions et d’autres définitions.
import : instruction qui lit un module et crée un objet module.
__main__ : le module __main__ est le module par défaut
composition : utiliser une expression comme une partie d’une expression plus grande, ou
une instruction comme une partie d’une instruction plus grande.
flot d’exécution : ordre dans lequel les instructions sont exécutées dans un programme.
diagramme de pile : représentation graphique d’une pile de fonctions, leurs variables, et
les valeurs qu’elles réfèrent.
traceback : liste de fonctions qui sont exéctuées, affichées quand une exception se
produit.
docstring : commentaire multiligne placé au début du corps d’une fonction, destiné à sa
documentation.
introspection : particularité du langage Python qui lui permet de s’auto-explorer.
objet fonction : valeur créée par la définition d’une fonction. Le nom de la fonction est une
variable qui réfère à un objet fonction.
objet module : valeur crée par une instruction import et qui fournit un accès aux
valeurs et fonctions définies dans le module.
notation point : (dot notation) syntaxe pour appeler une fonction ou utiliser une variable
définie dans un module en spécifiant le nom du module suivi d’un point, et du nom de la
fonction ou de la variable. Permet également d’accéder aux attributs d’un objet fonction
ou d’un objet module.
Programmation (Chap. 3) Fonctions 49 / 49
Chapitre 4. Instructions conditionnelles
Contenu du chapitre
1 Expressions booléennes
2 Instructions conditionnelles
3 Fonctions booléennes
4 Tuples
5 Entrées au clavier
6 Erreurs fréquentes et debug
7 Glossaire
Programmation (Chap. 4) Instructions conditionnelles 1 / 55
Expressions booléennes
Expressions booléennes
Programmation (Chap. 4) Instructions conditionnelles 2 / 55
Expressions booléennes
Expressions booléennes
Une expression booléenne est une expression qui retourne soit vrai,
soit faux.
le type bool possède deux valeurs spéciales : True (vrai) et
False (faux).
L’opérateur == compare deux opérandes et retourne True si elles
sont égales et False sinon.
>>> 5 == 5
True>>> 5 == 6
False
Programmation (Chap. 4) Instructions conditionnelles 3 / 55
Expressions booléennes
Le type bool
Les valeurs True et False sont de type bool, ce ne sont pas des str.
>>> type(True)
<type ’bool’>>>> type(False)<type ’bool’>
>>> type(’False’)
<type ’str’>>>> type(2 == 3)<type ’bool’>
Programmation (Chap. 4) Instructions conditionnelles 4 / 55
Expressions booléennes
Opérateurs de comparaison
L’opérateur == est un opérateur de comparaison. Il en existe 5 autres
qui retournent tous un bool :
x != y x n’est pas égal à y
x > y x est plus grand que y
x < y x est plus petit que y
x >= y x est plus grand ou égal à y
x <= y x est plus petit ou égal à y
Note : =< et => ne sont pas acceptés (erreurs de syntaxe)
Programmation (Chap. 4) Instructions conditionnelles 5 / 55
Expressions booléennes
Comparaison et assignation : ne pas confondre
Une erreur fréquente est de confondre l’opérateur de comparaison ==et l’opérateur d’assignation =
>>> x = 2>>> type(x == 3)
<type ’bool’>>>> res = (x == 3)>>> print resFalse>>> type(x = 3)TypeError: type() takes 1 or 3 arguments>>> res = (x = 3)SyntaxError: invalid syntax
Programmation (Chap. 4) Instructions conditionnelles 6 / 55
Expressions booléennes
Opérateurs logiques
Il y a 3 opérateurs logiques : and, or et not.
La sémantique de ces opérateurs est similaire à leur signification en
anglais : “et”, “ou” et “non”.
On construit des expressions booléennes en utilisant les opérateurs
de comparaison et logiques.
Par exemple,
x > 0 and x < 10
est vrai si et seulement si x est plus grand que 0 et plus petit que 10.
Programmation (Chap. 4) Instructions conditionnelles 7 / 55
Expressions booléennes
Exemples
x est compris entre 2 et 5
x >= 2 and x <= 5
x est plus petit que 4 ou plus grand que 10
x < 4 or x > 10
n est divisible par 2 ou 3
n % 2 == 0 or n % 3 == 0
x n’est pas divisible par 4
not (n % 4 == 0)
x n’est pas plus grand que y
not x > y
Comment réécrire les deux dernières expressions sans utiliser not ?
Remarque : not a une précédence plus faible que les opérateurs de
comparaisons
Programmation (Chap. 4) Instructions conditionnelles 8 / 55
Expressions booléennes
Valeurs non nulles
A priori les opérandes des opérateurs logiques devraient être des boolmais Python est moins strict que cela :
>>> 17 and True
True>>> 0 and True
0
Toute valeur non nulle est interprétée comme vraie, mais une valeur
nulle produit un comportement étrange !
Attention : cette flexibilité peut être pratique dans certains cas mais
est source de subtilités et confusion : à éviter (sauf si vous savez ce
que vous faites).
Programmation (Chap. 4) Instructions conditionnelles 9 / 55
Expressions booléennes
Opérateurs de comparaison multiples
Pour tester si x est compris entre 0 et 9, nous pouvons écrire
x >= 0 and x < 10
En mathématiques, on écrirait sans doute 0≤ x < 10.
En Python, l’expression booléenne suivante est également acceptée :
0 <= x < 10
Cependant, dans la plupart des langages de programmation, utiliser
des opérateurs de comparaison multiples est interdit (il faut les
composer avec “et”) : cette caractéristique de Python est inhabituelle.
Programmation (Chap. 4) Instructions conditionnelles 10 / 55
Expressions booléennes
Loi de De Morgan
Les expressions booléennes contenant un not appliqué sur une
expression composée d’un “et” ou d’un “ou” sont souvent difficiles à
comprendre.
not (0 < x and x < 1000)
“vrai quand il n’est pas vrai que x est plus grand que 0 et x est pluspetit que 1000”
La loi de De Morgan permet de simplifier une expression contenant
une négation sur deux termes joint par un “et” ou un “ou” :
not (A and B) est équivalent à (not A) or (not B)not (A or B) est équivalent à (not A) and (not B)
Remarquez comme les “et” et “ou” s’inversent.
Programmation (Chap. 4) Instructions conditionnelles 11 / 55
Expressions booléennes
Loi de De Morgan
Ainsi,
not (0 < x and x < 1000)“vrai quand il n’est pas vrai que x est plus grand que 0 et x est pluspetit que 1000”
devient
x <= 0 or x >= 1000“vrai quand x est plus petit ou égal à 0 ou plus grand ou égal à 1000”
Programmation (Chap. 4) Instructions conditionnelles 12 / 55
Expressions booléennes
Loi de De Morgan
Ainsi,
not (0 >= x or x >= 1000)“vrai quand il n’est pas vrai que x est plus petit ou égal à 0 et x estplus grand ou égal à 1000”
devient
x > 0 and x < 1000“vrai quand x est plus grand que 0 et plus petit que 1000”
Programmation (Chap. 4) Instructions conditionnelles 13 / 55
Instructions conditionnelles
Instructions conditionnelles
Programmation (Chap. 4) Instructions conditionnelles 14 / 55
Instructions conditionnelles
Exécution conditionnelle
Les programmes ont souvent besoin de tester certaines conditions et
de modifier leur comportements en fonction de celles-ci.
Les instructions conditionnelles permettent de le faire.
Syntaxe
if expression booléenne :instructions
Comportement
Les instructions (indentées) sont exécutées si l’expression booléenne
retourne vrai. Si ce n’est pas le cas, elles ne sont pas executées.
L’expression booléenne après le if est appellée la condition.
Programmation (Chap. 4) Instructions conditionnelles 15 / 55
Instructions conditionnelles
Exécution conditionnelle
Exemple
>>> x = 2>>> if x > 0:
print ’x est positif’
x est positif>>> if x < 0:
print ’x est negatif’
>>>
Comme pour le corps des fonctions, les instructions se trouvant
dans le corps d’un if doivent être indentées
Il n’y a pas de limite aux nombres d’instructions qui apparaissent
dans le corps d’un if
Programmation (Chap. 4) Instructions conditionnelles 16 / 55
Instructions conditionnelles
L’instruction pass
Dans le corps d’un if (et d’une fonction), il doit y avoir au moins une
instruction.
Occasionnellement, on a besoin (temporairement) d’un corps qui ne
contient pas d’instructions (par ex., pour y ajouter du code plus tard et
pouvoir se concentrer d’abord sur le reste).
Dans ce cas : on peut utiliser l’instruction pass, qui ne fait rien.
def f():pass
if x < 0:pass # TODO: gerer le cas ou x est negatif!
Programmation (Chap. 4) Instructions conditionnelles 17 / 55
Instructions conditionnelles
Exécution alternative
Une deuxième forme d’instruction if est l’exécution alternative, dans
laquelle il y a deux possibilités : la condition détermine laquelle est
exécutée.
Syntaxe
if expression booléenne :instructions A
else:instructions B
Comportement
Les instructions A ne sont exécutées que si l’expression booléenne (la
condition) retourne vrai. Si ce n’est pas le cas, les instructions B sont
exécutées.
Comme la condition est soit vraie, soit fausse, exactement une des deux
alternatives est exécutée. Ces alternatives sont appelées des branches car
elles permettent de définir différentes branches dans le flot d’exécution.
Programmation (Chap. 4) Instructions conditionnelles 18 / 55
Instructions conditionnelles
Exécution alternative
Exemple
x = 3
if x % 2 == 0:print ’x est pair’
else:print ’x est impair’
x est impair
Attention : respectez l’indentation. Il faut apprendre à voir comment la
gérer correctement avec IDLE, en mode interactif dans une console ou
avec un éditeur de texte en mode script.
Programmation (Chap. 4) Instructions conditionnelles 19 / 55
Instructions conditionnelles
Conditions chaînées
Parfois, il y a plus que deux possibilités. Pour créer plus de deux
branches on utilise des conditions chaînées.
Syntaxe
if expression booléenne :instructions A
elif expression booléenne :instructions B
. . .else:
instructions C
Comportement
Les instructions A ne sont exécutées que si la première condition est
vraie. Si ce n’est pas le cas, la deuxième condition est testée et ainsi
de suite, jusqu’au else qui est exécuté ssi toutes les conditions sont
fausses.
Programmation (Chap. 4) Instructions conditionnelles 20 / 55
Instructions conditionnelles
Conditions chaînées
Exemple
if x < y:print ’x est plus petit que y’
elif x > y:print ’x est plus grand que y’
else:print ’x et y sont égaux’
elif est une abréviation de “else if”
il n’y a pas de limite au nombre de elif
s’il y a un else, il se trouve à la fin, mais ce n’est pas obligatoire
(alors, si toutes les conditions sont fausses, il n’y a rien d’exécuté)
if x % 2 == 0:print ’x est pair’
elif x % 3 == 0:print ’x est divisible par 3’
Programmation (Chap. 4) Instructions conditionnelles 21 / 55
Instructions conditionnelles
Conditions imbriquées
Une condition peut être imbriquée dans une autre.
if x == y:print ’x et y sont égaux’
else:if x < y:
print ’x est plus petit que y’else:
print ’x est plus grand que y’
La première condition posséde deux branches. La première branche
contient une instruction seule. La seconde branche contient une autre
condition, qui possède également ses deux branches.
Bien que l’indentation montre la structure des conditions imbriquées,
elles deviennent vite difficiles à lire. En général, on essaye de les
éviter si possible.
Programmation (Chap. 4) Instructions conditionnelles 22 / 55
Instructions conditionnelles
Conditions imbriquées
Les opérateurs logiques permettent parfois de simplifier les conditions
imbriquées.
if x > 0:if x < 10:
print ’x is a positive single-digit number’
L’instruction print n’est exécutée que si les deux conditions sont
vraies. On peut donc écrire :
if x > 0 and x < 10:print ’x is a positive single-digit number’
Programmation (Chap. 4) Instructions conditionnelles 23 / 55
Instructions conditionnelles
Valeur de retour et instructions conditionnelles
Pour rappel, l’instruction return dans une fonction signifie “termine
immédiatement la fonction et utilise l’expression qui suit comme valeur
de retour”.
def valeur_absolue(x):if x < 0:
return -xelse
return x
Si une fonction qui doit retourner une valeur contient des instructions
conditionnelles, il faut s’assurer que toutes les possibilités rencontrent
une instruction return.
>>> def valeur_absolue(x):if x < 0:
return -xif x > 0:
return x
>>> print valeur_absolue(0)
NoneProgrammation (Chap. 4) Instructions conditionnelles 24 / 55
Instructions conditionnelles
Valeur de retour et instructions conditionnelles
Une instruction return qui n’est pas suivie d’une expression peut être
utilisée pour sortir d’une fonction prématurément. Dans ce cas, la
valeur spéciale None est automatiquement retournée.
def aire(r):if r < 0:
returnreturn math.pi * r ** 2
Programmation (Chap. 4) Instructions conditionnelles 25 / 55
Fonctions booléennes
Fonctions booléennes
Programmation (Chap. 4) Instructions conditionnelles 26 / 55
Fonctions booléennes
Fonctions booléennes
Les fonctions peuvent retourner des booléens, ce qui est souvent très
utile pour cacher des tests compliqués dans des conditions ou rendre
le code plus clair.
Exemple :
>>> def is_divisible(x,y):if x % y == 0:
return Trueelse:
return False
>>> is_divisible(6,4)
False>>> is_divisible(6,3)
True
Programmation (Chap. 4) Instructions conditionnelles 27 / 55
Fonctions booléennes
Fonctions booléennes
C’est une bonne habitude de nommer les fonctions booléennes
comme des questions dont la réponse est “oui” (True) ou “non”
(False) : est_pair, est_premier, is_positive.
Les fonctions booléennes augmentent la lisibilité des instructions
conditionnelles.
if is_divisible(x,2) and is_positive(x):print ’x est un nombre pair positif’
Comparer explicitement la valeur de retour d’une fonction booléenne
est inutile et alourdit le code.
if is_divisible(x,y) == True:print ’x est divisible par y’
est donc à proscrire.
Programmation (Chap. 4) Instructions conditionnelles 28 / 55
Fonctions booléennes
Fonctions booléennes
Comme un opérateur de comparaison retourne une valeur booléenne,
on peut écrire is_divisible de manière plus concise :
def is_divisible(x,y):return x % y == 0
Programmation (Chap. 4) Instructions conditionnelles 29 / 55
Fonctions booléennes
Evaluation “lazy” (évaluation paresseuse)
Lors d’un test and ou or, Python effectue une évaluation paresseuse
(lazy evaluation) :
A and B : si A est faux, le test B n’est pas évalué, retourne False
A or B : si A est vrai, le test B n’est pas évalué, retourne True
Utile si le test B est par exemple un appel à une fonction booléenne qui
prend du temps à être calculée.
Programmation (Chap. 4) Instructions conditionnelles 30 / 55
Fonctions booléennes
Evaluation “paresseuse”
>>> def is_divisible(x,y):print ’=> test divisible’return x % y == 0
>>> def is_positive(x):print ’=> test positive’return x > 0
>>> def is_even_and_positive(x):if is_divisible(x,2) and is_positive(x):
print ’even AND positive’else:
print ’NOT even AND positive’
>>> def is_even_or_positive(x):if is_divisible(x,2) or is_positive(x):
print ’even OR positive’else:
print ’NOT even OR positive’
Suite slide suivant. . .Programmation (Chap. 4) Instructions conditionnelles 31 / 55
Fonctions booléennes
Evaluation “paresseuse”
A and B : si A est faux, le test B n’est pas évalué, retourne False
>>> is_even_and_positive(4)=> test divisible=> test positiveeven AND positive>>> is_even_and_positive(5)=> test divisibleNOT even AND positive>>> is_even_and_positive(-2)=> test divisible=> test positiveNOT even AND positive
Suite slide suivant. . .
Programmation (Chap. 4) Instructions conditionnelles 32 / 55
Fonctions booléennes
Evaluation “paresseuse”
A or B : si A est vrai, le test B n’est pas évalué, retourne True
>>> is_even_or_positive(4)=> test divisibleeven OR positive>>> is_even_or_positive(5)=> test divisible=> test positiveeven OR positive>>> is_even_or_positive(-3)=> test divisible=> test positiveNOT even OR positive
Programmation (Chap. 4) Instructions conditionnelles 33 / 55
Tuples
Tuples
Programmation (Chap. 4) Instructions conditionnelles 34 / 55
Tuples
Tuples
En mathématiques, on représente un point dans Rncomme un
n-uplet : collection ordonnée (séquence) de n coordonnées. Par
exemple, l’origine du plan est (0,0) et un point dans l’espace peut être
représenté par (x ,y ,z) où x , y et z sont ses coordonnées dans les 3
dimensions de l’espace.
En Python, un tuple peut être vu et écrit de manière similaire :
séquence de plusieurs valeurs séparées par des virgules et mises
entre parenthèses.
>>> x = 2>>> y = 3>>> point1 = (x, y)>>> point2 = (4, 5)>>> type(point1)<type ’tuple’>>>> print point1, point2(2, 3) (4, 5)
Programmation (Chap. 4) Instructions conditionnelles 35 / 55
Tuples
Tuples
On peut assigner les valeurs d’un tuple dans un autre (possédant le
même nombre de valeurs)
>>> (a, b) = point1>>> print a2>>> print b3
Ceci permet de réécrire notre premier algorithme (échanger deux
valeurs) très simplement et sans utiliser de variables temporaires :
>>> a = 17>>> b = 21>>> (b, a) = (a, b)>>> print a, b21 17
Programmation (Chap. 4) Instructions conditionnelles 36 / 55
Tuples
Tuples
Les valeurs d’un tuple ne doivent pas être du même type.
>>> matricule = 142543>>> nom = ’John Doe’>>> student = (nom, matricule)>>> print student(’John Doe’, 142543)
Une fonction peut prendre un tuple en paramètre.
>>> def sum_pair(t):(x, y) = treturn x + y
>>> sum_pair(point1)5
Programmation (Chap. 4) Instructions conditionnelles 37 / 55
Tuples
Tuples
Une fonction peut retourner un tuple, ce qui lui permet par exemple de
retourner plusieurs valeurs :
>>> def min_max(x,y,z):min = xmax = xif y < min:
min = yif z < min:
min = zif y > max:
max = yif z > max:
max = zreturn (min,max)
>>> (petit, grand) = min_max(6,2,3)>>> print petit, grand2 6
Programmation (Chap. 4) Instructions conditionnelles 38 / 55
Entrées au clavier
Entrées au clavier
Programmation (Chap. 4) Instructions conditionnelles 39 / 55
Entrées au clavier
Entrées au clavier
La fonction raw_input permet d’obtenir des données entrées au
clavier par l’utilisateur.
quand elle est appelée, le programme s’arrête et attend que
l’utilisateur entre quelque chose au clavier
quand l’utilisateur appuye sur Return ou Enter, le programme
reprend et raw_input retourne ce que l’utilisateur a entré sous la
forme d’un str
>>> data = raw_input()qu’attends-tu?>>> print data
qu’attends-tu?
Programmation (Chap. 4) Instructions conditionnelles 40 / 55
Entrées au clavier
Entrées au clavier
Avant d’attendre une entrée de l’utilisateur, il est utile de lui préciser ce
qu’on attend de lui ! La fonction raw_input prend un argument de type
str pour ce faire.
>>> name = raw_input(’Quel est votre nom ? ’)
Quel est votre nom ? John Doe>>> print name
John Doe
Programmation (Chap. 4) Instructions conditionnelles 41 / 55
Entrées au clavier
Entrées au clavier
Le caractère spécial \n peut être utilisé dans un str et représente une
nouvelle ligne.
>>> name = raw_input(’Quel est votre nom ?\n’)
Quel est votre nom ?John Doe>>> print name
John Doe
Programmation (Chap. 4) Instructions conditionnelles 42 / 55
Entrées au clavier
Entrées au clavier
Si vous voulez obtenir un nombre entier ou réel, il faut convertir le strretourné par raw_input.
>>> prompt = ’Quel est votre age ?\n’>>> data = raw_input(prompt)
Quel est votre age ?19>>> print data, type(data)19 <type ’str’>>>> age = int(data)>>> print ’Vous allez bientot avoir’, (age + 1), ’ans’
Vous allez bientot avoir 20 ans
Programmation (Chap. 4) Instructions conditionnelles 43 / 55
Entrées au clavier
Entrées au clavier
Si l’utilisateur entre quelque chose qui ne peut être correctement
converti, on obtient une erreur.
>>> prompt = ’Quel est votre taille ?\n’>>> data = raw_input(prompt)
Quel est votre taille ?Heu.... en centimetres ou en metres ?>>> taille = float(data)
ValueError: invalid literal for float()
Rappel : Comment appelle-t-on ce type d’erreurs ?
On verra comment gérer cela plus tard.
Programmation (Chap. 4) Instructions conditionnelles 44 / 55
Entrées au clavier
Interaction avec l’utilisateur : exemple
Problème
Ecrire un programme qui permette de calculer l’aire ou la
circonférence d’un cercle de rayon r .
Programmation (Chap. 4) Instructions conditionnelles 45 / 55
Entrées au clavier
Interaction avec l’utilisateur : exemple
Une solution :
import math
def aire(r):return math.pi * r ** 2
def circonference(r):return math.pi * 2.0 * r
def print_res(res,type):print type, ’=’, res
suite slide suivant. . .
Programmation (Chap. 4) Instructions conditionnelles 46 / 55
Entrées au clavier
Interaction avec l’utilisateur : exemple
prompt = ’Veuillez entrer le rayon du cercle...\n’rayon = float(raw_input(prompt))
if rayon < 0.0:print ’Je vais avoir du mal a calculer ca...’
else:prompt = ’Voulez-vous connaitre l\’aire ou la circonference ?\n’typeCalcul = raw_input(prompt)if typeCalcul == ’aire’ or typeCalcul == ’Aire’:
res = aire(rayon)print_res(res, typeCalcul)
elif typeCalcul == ’circonference’ or typeCalcul == ’Circonference’:res = circonference(rayon)print_res(res, typeCalcul)
else:print ’Je ne comprend que \"aire\" ou \"circonference\"’
Programmation (Chap. 4) Instructions conditionnelles 47 / 55
Erreurs fréquentes et debug
Erreurs fréquentes et debug
Programmation (Chap. 4) Instructions conditionnelles 48 / 55
Erreurs fréquentes et debug
Erreurs fréquentes et debug
Pour les erreurs de syntaxe et les exceptions, Python affiche un
message : l’information la plus utile dans ce message est :
le type d’erreur (dernière ligne) : SyntaxError, RuntimeError, . . .
l’endroit où cette erreur apparaît.
Cependant, il n’est pas toujours facile de trouver l’endroit exact du
code qu’il faut corriger avec ces informations.
Programmation (Chap. 4) Instructions conditionnelles 49 / 55
Erreurs fréquentes et debug
Erreurs fréquentes et debug
Erreur d’indentation :
>>> x = 5>>> y = 6
File "<pyshell#1>", line 1y = 6
ˆ
IndentationError: unexpected indent
Programmation (Chap. 4) Instructions conditionnelles 50 / 55
Erreurs fréquentes et debug
Erreurs fréquentes et debug
Exceptions sur les valeurs dans des opérations mathématiques (ici,
dues à la division entière)
>>> import math>>> signal = 9>>> noise = 10>>> ratio = signal / noise>>> decibels = 10 * math.log10(ratio)
File "<pyshell#6>", line 1, in <module>decibels = 10 * math.log10(ratio)
ValueError: math domain error>>> invert = 1 / ratioFile "<pyshell#7>", line 1, in <module>
invert = 1 / ratioZeroDivisionError: integer division or modulo by zero
Ici: le message n’indique pas où se trouve le problème (qui devrait
être corrigé lors du calcul de ratio)
Programmation (Chap. 4) Instructions conditionnelles 51 / 55
Erreurs fréquentes et debug
Erreurs fréquentes et debug
Si on obtient une exception ou une erreur sémantique lors de l’appel
d’une fonction, il y a 3 possibilités :
problème au niveau des arguments passés à la fonction : une
précondition1
est violée (par exemple un argument dont le type
n’est pas géré par la fonction, ou une valeur négative pour un
calcul sur des nombres positifs)
problème au niveau du code de la fonction (corps) : une
postcondition2
est violée (par exemple, une erreur sémantique
dans le corps de la fonction qui produit un mauvais résultat)
problème au niveau de la valeur de retour (est-elle correctement
utilisée ?)
1conditions que la fonction suppose vraies pour les entrées, avant d’exécuter son
travail
2conditions qu’une fonction garantit si les préconditions sont respectées, la validité
de son résultat
Programmation (Chap. 4) Instructions conditionnelles 52 / 55
Erreurs fréquentes et debug
Erreurs fréquentes et debug
Pour déboguer une fonction :
Pour identifier une erreur due à une précondition violée, vous
pouvez afficher la valeurs des paramètres en début de fonction
(et peut-être leur types). Vous pouvez également écrire du code
qui vérifie les préconditions explicitement.
Si les paramètres semblent corrects, ajoutez un affichage des
valeurs retournées avant chaque instruction return. Si possible,
vérifiez le résultat que l’on devrait obtenir à la main (appelez la
fonction avec des valeurs qui sont faciles à vérifier).
Si la fonction semble correcte, regardez l’appel de la fonction et
vérifiez que la valeur de retour est utilisée correctement (ou
qu’elle est vraiment utilisée).
Ajouter des affichages au début et à la fin d’une fonction peut
aider à rendre plus visible le flot d’exécution.
Programmation (Chap. 4) Instructions conditionnelles 53 / 55
Glossaire
Glossaire
Programmation (Chap. 4) Instructions conditionnelles 54 / 55
Glossaire
Glossaire
expression booléenne : expression qui retourne soit vrai (True), soit faux ( False)
opérateur de comparaison : un des opérateurs qui comparent ses opérandes : ==, !=, <,
>, <= et >=.
opérateur logique : un des opérateurs qui combinent des expressions booléennes : and,
or et not.
instruction conditionnelle : instruction qui contrôle le flot d’exécution en fonction de
certaines conditions
condition : expression booléenne dans une instruction conditionnelle qui détermine quelle
branche doit être exécutée
conditions chaînées : instruction conditionnelle avec une série de branches alternatives
conditions imbriquées : instruction conditionnelle qui apparaît à l’intérieur d’une autre
instruction conditionnelle
Programmation (Chap. 4) Instructions conditionnelles 55 / 55
Chapitre 5. Itérations et chaînes de caractères
Contenu du chapitre1 Incrémenter et décrémenter
2 Boucles while
3 Erreurs d’approximation
4 Un str est une séquence immuable de caractères
5 Boucles for
6 Invocation de méthodes sur les chaînes
7 Opérateurs supplémentaires sur les chaînes
8 Jouer avec les mots
9 Erreurs fréquentes et debug
10 Glossaire
Programmation (Chap. 5) Itérations et chaînes de caractères 1 / 98
Incrémenter et décrémenter
Incrémenter et décrémenter
Programmation (Chap. 5) Itérations et chaînes de caractères 2 / 98
Incrémenter et décrémenter
Assignation multiple
Comme nous l’avons vu, on peut réassigner une valeur à une variable(assignation multiple)
bruce = 5print bruce,bruce = 7print bruce
5 7
bruce
5
7
Remarque : la virgule à la fin de la première instruction printsupprime le retour à la ligne.
Programmation (Chap. 5) Itérations et chaînes de caractères 3 / 98
Incrémenter et décrémenter
Incrémenter et décrémenter
Une des formes les plus courantes de l’assignation multiple : la mise àjour d’une variable, où la nouvelle valeur de la variable dépend de sonancienne valeur.
x = x + 1
“obtenir la valeur courante de x, ajouter 1, et mettre à jour x avec la
nouvelle valeur.”
Programmation (Chap. 5) Itérations et chaînes de caractères 4 / 98
Incrémenter et décrémenter
Incrémenter et décrémenter
Si on met à jour une variable qui n’a pas été créée, cela provoque uneerreur (Python évalue le côté droit de l’assignation avant d’assigner lavaleur au membre gauche).
>>> x = x + 1
NameError: name ’x’ is not defined
Avant de mettre à jour une variable, il faut qu’elle ait été initialisée, engénéral avec une simple assignation.
>>> x = 0>>> x = x + 1
Programmation (Chap. 5) Itérations et chaînes de caractères 5 / 98
Incrémenter et décrémenter
Incrémenter et décrémenter
On dit qu’on incrémente une variable quand on met à jour une variableen lui ajoutant un nombre (souvent 1)
step = 2x = x + 1y = y + step
On dit qu’on décrémente une variable quand on met à jour unevariable en lui soustrayant un nombre (souvent 1)
n = n - 1
Programmation (Chap. 5) Itérations et chaînes de caractères 6 / 98
Boucles while
Boucles while
Programmation (Chap. 5) Itérations et chaînes de caractères 7 / 98
Boucles while
Boucles while
Les boucles ou structures itératives permettent d’automatiser destâches répétitives.
Répéter des tâches identiques ou similaires sans erreur est quelquechose que les ordinateurs font bien, contrairement aux humains.
Une première forme de structure itérative : boucle while (“tant que”)
Syntaxewhile expression booléenne :
instructions
ComportementLes instructions (indentées) sont exécutées de manière répétée tantque l’expression booléenne (la condition) retourne vrai.
Programmation (Chap. 5) Itérations et chaînes de caractères 8 / 98
Boucles while
Boucles while
Exemple simple: fonction countdown :
>>> def countdown(n):while n > 0:
print nn = n - 1
print ’Boum’>>> countdown(3)321Boum
Programmation (Chap. 5) Itérations et chaînes de caractères 9 / 98
Boucles while
Un problème illustratif
DéfinitionPour un nombre naturel strictement positif n donné, on peut appliquerl’opération suivante :
si n est pair, on le divise par 2;
si n est impair, on le multiplie par 3 et on ajoute 1.
La suite de Syracuse de n est la suite de nombres naturels obtenue enrépétant successivement l’opération ci-dessus.
Exemple : à partir de 7, on obtient la suite7,22,11,34,17,52,26,13,40,20,10,5,16,8,4,2,1,4,2,1,4,2,1, . . .
Programmation (Chap. 5) Itérations et chaînes de caractères 10 / 98
Boucles while
Un problème illustratif
Une conjecture est un énoncé qui semble vrai, mais pour lequel on n’apas encore pu
prouver mathématiquement sa validité;
trouver de contre-exemple montrant qu’il est faux.
Conjecture (Conjecture de Syracuse ou de Collatz)La suite de Syracuse d’un nombre naturel strictement positif
quelconque n atteint toujours 1.
Une fois 1 atteint, la sous-suite (4,2,1) se répète.
ProblèmeEcrire un programme qui affiche la suite de Syracuse d’un nombre n
jusqu’à 1, ou qui affiche un message si 1 n’est pas encore obtenuaprès une limite donnée.
Programmation (Chap. 5) Itérations et chaînes de caractères 11 / 98
Boucles while
Boucles while
Voici une fonction itérative qui affiche la suite de Syracuse d’unnombre entier strictement positif jusqu’à 1 non compris.
>>> def sequence(n):while n != 1:
print n,if n % 2 == 0:
n = n / 2else:
n = n * 3 + 1
>>> sequence(7)7 22 11 34 17 52 26 13 40 20 10 5 16 8 4 2
Programmation (Chap. 5) Itérations et chaînes de caractères 12 / 98
Boucles while
Boucles while
On peut presque “lire” une instruction while comme si c’était del’anglais.
while n > 0:print nn = n - 1
print ’Boum’
“While n is greater than 0, print (the value of) n (and then) decrement n
by 1. (When you reach 0), print (the word) Boum”
“Tant que n est plus grand que 0, afficher la valeur de n, puis
décrémenter n. Quand on atteint 0, afficher le mot Boum”
Programmation (Chap. 5) Itérations et chaînes de caractères 13 / 98
Boucles while
Boucles while
Plus formellement, voici le flot d’exécution d’une instruction while :1 Evaluer la condition (True ou False)2 Si la condition est fausse, sortir de l’instruction while et continuer
l’exécution à l’instruction suivante3 Si la condition est vraie, exécuter le corps (instructions indentées)
de l’instruction while, puis retourner à l’étape 1
D’où le nom de boucle : dans l’étape 3, on boucle le flot d’exécutionvers l’étape 1.
Programmation (Chap. 5) Itérations et chaînes de caractères 14 / 98
Boucles while
Boucles infinies
Le corps d’une boucle doit mettre à jour les valeurs d’une ou plusieursvariables de telle sorte que la condition soit évaluée à False à unmoment donné, pour que la boucle se termine.
Dans le cas de countdown, on peut prouver facilement que si lespréconditions sont respectées (n est un entier positif fini) alors laboucle se termine.
On parle de preuve de l’arrêt d’une boucle ou d’un algorithmecontenant des boucles. Bien souvent, ces preuves se font parrécurrence.
Programmation (Chap. 5) Itérations et chaînes de caractères 15 / 98
Boucles while
Prouver qu’une boucle s’arrête
def countdown(n):while n > 0: #1
print n #2n = n - 1 #3
print ’Boum’
Preuve de l’arrêt de countdown (par récurrence).On suppose que n est un entier ≥ 0 (fini). Si n→ 0, la condition (1) estévaluée à faux, et la boucle se termine.Supposons que la boucle s’arrête pour n→ k (k ≥ 0) , et prouvons le pourn→ k +1. Quand n→ k +1, l’instruction 2 est exécutée (affiche k +1), puisn→ k lors de l’instruction 3.La boucle est répétée avec n→ k . Celle-ci s’arrêtera par hypothèse derécurrence.
Programmation (Chap. 5) Itérations et chaînes de caractères 16 / 98
Boucles while
Prouver qu’une boucle s’arrêteProuver qu’une boucle s’arrête n’est pas toujours facile.
def sequence(n):while n != 1:
print n,if n % 2 == 0:
n = n / 2else:
n = n * 3 + 1
n diminue quand il est pair, mais augmente quand il est impair :une preuve simple par récurrence ne marcherait pas
pour certaines valeurs de n, il est facile de prouver qu’on atteint 1(par ex., si n est une puissance de 2)
prouver le cas général (tout entier fini strictement positif)équivaudrait à prouver la conjecture de Syracuse, qui constitueun problème ouvert depuis 1952 !
Programmation (Chap. 5) Itérations et chaînes de caractères 17 / 98
Boucles while
Instruction breakL’instruction break provoque l’arrêt de la boucle en exécution.
Exemple : on veut obtenir des entrées de l’utilisateur, jusqu’à ce qu’ilentre fin.
text = ’’ # chaine videwhile True:
line = raw_input(’> ’)if line == ’fin’:
breakelse:
text = text + line + ’\n’
print text
Remarque importante : on vous demande de vous passer de cetteinstruction (toujours possible) qui généralement rend le code moins“propre” .
Programmation (Chap. 5) Itérations et chaînes de caractères 18 / 98
Boucles while
Solution sans break
text = ’’ # chaine videline = raw_input(’> ’)while line != ’fin’:
text = text + line + ’\n’line = raw_input(’> ’)
print text
Programmation (Chap. 5) Itérations et chaînes de caractères 19 / 98
Boucles while
La méthode de Héron
La méthode de Héron permet de calculer la racine carrée d’unnombre a.
Si x est une approximation de√
a, alorsx+a/x
2 est une
meilleure approximation de√
a.
Exercice : écrire une fonction itérative qui calcule la racine carrée d’unnombre
Avant d’écrire l’algorithme examinons quelques petits soucis liés auxcalculs sur ordinateurs.
Programmation (Chap. 5) Itérations et chaînes de caractères 20 / 98
Erreurs d’approximation
Erreurs d’approximation
Programmation (Chap. 5) Itérations et chaînes de caractères 21 / 98
Erreurs d’approximation
Conversion de réels en entiersPour rappel, si x et y sont deux entiers positifs, le résultat de leurdivision est la partie entière de x
y.
D’autre part, la fonction int avec un nombre réel x en argumentretourne la partie entière de x .
>>> 1 / 20>>> int(0.5)0
Attention : en réalité, la division entière calcule un plancher1, alors queint tronque la partie décimale. Ainsi, avec des nombres négatifs, lescomportements sont différents.
>>> - 1 / 2-1>>> int(-0.5)0
1le plancher de x est le plus grand entier i tel que i ≤ x .Programmation (Chap. 5) Itérations et chaînes de caractères 22 / 98
Erreurs d’approximation
Conversion de réels en entiers
Soient p et q deux entiers. Si
d = p / qr = p % q
alors il est logique que d×q + r donne p (car d est le résultat de ladivision et r le reste).
Le comportement de la division entière avec un nombre négatif estcohérent avec ceci.
>>> p = 16>>> q = 3>>> d = p / q>>> r = p % q>>> print d, r, d * q + r5 1 16
>>> p = -16>>> q = 3>>> d = p / q>>> r = p % q>>> print d, r, d * q + r-6 2 -16
Programmation (Chap. 5) Itérations et chaînes de caractères 23 / 98
Erreurs d’approximation
Conversion de réels en entiersSachant que int tronque un réel, les fonctions suivantes peuvent êtreutiles avant de convertir un entier en réel (notez qu’elles retournent unréel, mais qui correspond à un entier).
>>> import math>>> x = 1.34>>> y = -3.29>>> z = 2.0
Plancher (plancher de x = plus grand entier i tel que i ≤ x)>>> print math.floor(x), math.floor(y), math.floor(z)1.0 -4.0 2.0
Plafond (plafond de x = plus petit entier i tel que i ≥ x)>>> print math.ceil(x), math.ceil(y), math.floor(z)2.0 -3.0 2.0
Arrondi (entier le plus proche)>>> print round(x), round(y), round(1.5), round(-1.5)1.0 -3.0 2.0 -2.0
Remarquez que le plafond et le plancher d’un nombre entier sont égaux,sinon, ils diffèrent de 1.Programmation (Chap. 5) Itérations et chaînes de caractères 24 / 98
Erreurs d’approximation
Représentation binaire
Les chiffres utilisés en base 10 vont de 0 à 9, en base 2, nous n’avonsbesoin que de 0 et de 1 (binaire).
Les nombres sont représentés en base 2 sur un ordinateur.
Processeurs composés de millions de transistors (imprimés sur uncircuit électronique) : chacun ne gère qu’un bit : 0 (le courant ne passepas) et 1 (le courant passe).
Programmation (Chap. 5) Itérations et chaînes de caractères 25 / 98
Erreurs d’approximation
Représentation binaire
Exemple : le nombre 35 est écrit
35 en base 10 car
3×101 +5×100 = 30+5 = 35;
100011 en base 2 car
1×25 +0×24 +0×23 +0×22 +1×21 +1×20 = 32+2+1 = 35.
Exercice : représenter 13 en base 2.
en base 2 : 1101 car
1×23 +1×22 +0×21 +1×20 = 8+4+1 = 13.
Programmation (Chap. 5) Itérations et chaînes de caractères 26 / 98
Erreurs d’approximation
Représentation binaire
Exemple : le nombre 35 est écrit
35 en base 10 car
3×101 +5×100 = 30+5 = 35;
100011 en base 2 car
1×25 +0×24 +0×23 +0×22 +1×21 +1×20 = 32+2+1 = 35.
Exercice : représenter 13 en base 2.
en base 2 : 1101 car
1×23 +1×22 +0×21 +1×20 = 8+4+1 = 13.
Programmation (Chap. 5) Itérations et chaînes de caractères 26 / 98
Erreurs d’approximation
Représentation binaireIl en va de même pour les fractions.
Exemple : la fraction 14 est écrite
0.25 en base 10 car
2101 +
5102 =
210
+5
100=
25100
=14;
0.01 en base 2 car021 +
122 =
14.
Exercice : représenter 118 en base 2.
en base 2 : 1.011 car
(1×20)+021 +
122 +
123 = 1+
14
+18
=88
+28
+18
=118
.
Programmation (Chap. 5) Itérations et chaînes de caractères 27 / 98
Erreurs d’approximation
Représentation binaireIl en va de même pour les fractions.
Exemple : la fraction 14 est écrite
0.25 en base 10 car
2101 +
5102 =
210
+5
100=
25100
=14;
0.01 en base 2 car021 +
122 =
14.
Exercice : représenter 118 en base 2.
en base 2 : 1.011 car
(1×20)+021 +
122 +
123 = 1+
14
+18
=88
+28
+18
=118
.
Programmation (Chap. 5) Itérations et chaînes de caractères 27 / 98
Erreurs d’approximation
Erreurs d’approximation
En base 10, la fraction 13 ne peut être représentée de manière exacte,
on doit l’approximer car le terme 3 se répète de manière infinie :
0.33333333333333333 . . .
Par contre, la fraction 110 peut être représentée de manière exacte : 0.1
Cependant, en base 2, la fraction 110 est représentée par :
0.000110011001100110011 . . .
où le terme 0011 se répète de manière infinie.
Programmation (Chap. 5) Itérations et chaînes de caractères 28 / 98
Erreurs d’approximation
Erreurs d’approximation
Le nombre de bits utilisés par l’ordinateur pour représenter un nombreétant fini explique pourquoi on obtient :
>>> 0.10.10000000000000001
ce n’est donc pas un “bug” de Python (en réalité Python n’y estpour rien)
l’erreur peut varier d’une machine à l’autre en fonction du nombrede bits alloués pour y représenter un nombre
tout calcul impliquant un float peut provoquer une erreurd’approximation
Programmation (Chap. 5) Itérations et chaînes de caractères 29 / 98
Erreurs d’approximation
Erreurs d’approximationOn ne peut pas éviter les erreurs d’approximation, mais on peutcontrôler la manière dont les nombres sont affichés :
par défaut, un float est affiché en utilisant 17 chiffres significatifs(peut varier)>>> 0.10.10000000000000001>>> math.pi3.1415926535897931
la fonction str convertit un nombre en chaînes de caractères enutilisant 12 chiffres significatifs (peut varier)>>> str(math.pi)’3.14159265359’>>> str(0.1)’0.1’
l’instruction print affiche un nombre en utilisant 12 chiffressignificatifs (peut varier)>>> print math.pi3.14159265359>>> print 0.10.1Programmation (Chap. 5) Itérations et chaînes de caractères 30 / 98
Erreurs d’approximation
Erreurs d’approximation
la syntaxe suivante permet de préciser le nombre de chiffresaprès la virgule affichés par print>>> print ’%.4f’ % math.pi3.1416>>> print ’%.50f’ % math.pi3.14159265358979311599796346854418516159057617187500>>> x = 0.1>>> print ’%.2f’ % x0.10
Remarque : cela ne change que l’affichage, les erreursd’approximation sont toujours là.
>>> print ’%.50f’ % x0.10000000000000000555111512312578270211815834045410
Programmation (Chap. 5) Itérations et chaînes de caractères 31 / 98
Erreurs d’approximation
Tester l’égalité de deux floatQuand on veut tester l’égalité de deux float, il faut donc éviterd’utiliser ==>>> x = 0.1>>> y = (math.sqrt(math.sqrt(x))) ** 4>>> print x, y0.1 0.1>>> x == yFalse>>> x0.10000000000000001>>> y0.099999999999999992
Solution : écrire une fonction qui teste si x et y sont suffisammentproches, à ε = 10−7 près par exemple.
de façon absolue si x et y ne sont pas “petits” (proche de 0):abs(y - x) < epsilonde façon relative si x et y sont “petits” (proche de 0):abs(y - x) < epsilon *(abs(x)+abs(y))
Programmation (Chap. 5) Itérations et chaînes de caractères 32 / 98
Erreurs d’approximation
La méthode de Héron
Une solution :
def almost_equal(x, y, epsilon = 10 ** -7):return abs(y - x) < epsilon
def square_root(a):x = 0.0y = 1.0while not almost_equal(x, y):
x = yy = (x + a / x) / 2
return x
Programmation (Chap. 5) Itérations et chaînes de caractères 33 / 98
Un str est une séquence immuable de caractères
Un str est une séquenceimmuable de caractères
Programmation (Chap. 5) Itérations et chaînes de caractères 34 / 98
Un str est une séquence immuable de caractères
Une chaîne de caractères est une séquenceUn str est une séquence de caractères (collection ordonnée).
On peut accéder à chaque caractère en utilisant l’opérateur [.](crochets droits, “bracket”).
Exemple :>>> fruit = ’banane’>>> lettre = fruit[1]>>> type(lettre)
<type ’str’>
fruit[1] signifie “accéder au caractère dont l’indice est 1 de la chaîneréférencée par fruit”.
L’opérateur [x] — où x est un entier — retourne un str constitué d’unseul caractère : celui se trouvant à l’indice x
>>> print lettrea
Est-ce ce que vous attendiez ?Programmation (Chap. 5) Itérations et chaînes de caractères 35 / 98
Un str est une séquence immuable de caractères
Indices d’une chaîne de caractères
>>> fruit = ’banane’>>> lettre = fruit[1]>>> print lettre
a
La première lettre du mot banane est “b”, pas “a”. Mais, eninformatique, les indices d’une séquence commencent souvent à 0.C’est le cas également en Python.
>>> lettre = fruit[0]>>> print lettre
b
Intuition : indice = nombre de caractères quiséparent le caractère considéré du début dela séquence
Programmation (Chap. 5) Itérations et chaînes de caractères 36 / 98
Un str est une séquence immuable de caractères
Indices d’une chaîne de caractères
>>> fruit = ’banane’>>> lettre = fruit[1]>>> print lettre
a
La première lettre du mot banane est “b”, pas “a”. Mais, eninformatique, les indices d’une séquence commencent souvent à 0.C’est le cas également en Python.
>>> lettre = fruit[0]>>> print lettre
b
Intuition : indice = nombre de caractères quiséparent le caractère considéré du début dela séquence 5
b a n a n e
0 1 2 3 4
Programmation (Chap. 5) Itérations et chaînes de caractères 36 / 98
Un str est une séquence immuable de caractères
Indices d’une chaîne de caractères
La longueur d’une séquence est son nombre d’éléments. Elle peutêtre obtenue grâce à la fonction len (‘length”).
Indices d’une chaîne de longueur n : entier allant de 0 à n−1.
>>> mot = ’computer’>>> len(mot)8>>> mot[0]’c’>>> mot[7]’r’>>> mot[8]IndexError: string index out of range>>> mot[1.5]TypeError: string indices must be integers, not float
Programmation (Chap. 5) Itérations et chaînes de caractères 37 / 98
Un str est une séquence immuable de caractères
Indices d’une chaîne de caractèresOn peut également accéder aux éléments grâce à un indice inversé :le dernier caractère s’obtient avec l’indice −1, l’avant dernier avec −2,etc.
Indices inversés d’une chaîne de longueur n : entier allant de−1 à −n.
>>> mot = ’banane’>>> mot[-1]’e’>>> mot[-6]’b’>>> mot[-7]IndexError: string index out of range
!1
b a n a n e
0 1 2 3 4 5
!6 !5 !4 !3 !2
Programmation (Chap. 5) Itérations et chaînes de caractères 38 / 98
Un str est une séquence immuable de caractères
Indices d’une chaîne de caractères
La fonction suivante accède aux caractères en utilisant tout l’intervalledes indices valides [−n,n−1].
>>> def traversal(s):n = len(s)index = - nwhile index < n:
print s[index], # la virgule supprime le retour a la ligneindex = index + 1
>>> traversal(’algorithme’)a l g o r i t h m e a l g o r i t h m e
Remarque : on préfère bien souvent la condition while index < nplutôt que while index <= n - 1 équivalente. Quand index == n, lacondition est fausse, et on sort de la boucle.
Programmation (Chap. 5) Itérations et chaînes de caractères 39 / 98
Un str est une séquence immuable de caractères
Exercices
ProblèmeEcrire une fonction qui prend une chaîne en argument et qui retourneune chaîne constituée des caractères de la chaîne dont l’ordre estinversé.
ProblèmeEcrire une fonction qui prend un nombre binaire (entier) en argument(représenté sous la forme d’une chaîne de caractères) et qui retournece nombre en base 10 (sous la forme d’un entier). Cette fonctionretourne None si la chaîne ne représente pas un nombre binaire.
Programmation (Chap. 5) Itérations et chaînes de caractères 40 / 98
Un str est une séquence immuable de caractères
Exercices
Inversion d’une chaîne :
>>> def invert(s):res = ’’ # chaine viden = len(s)index = - 1while index >= -n:
res = res + s[index]index = index - 1
return res
>>> invert(’algorithme’)’emhtirogla’
Programmation (Chap. 5) Itérations et chaînes de caractères 41 / 98
Un str est une séquence immuable de caractères
Exercices
Conversion binaire→ décimal
>>> def bin2dec(b):i = 0n = len(b)puissance = 0index = -1while index >= -n:
if b[index] == ’1’:i = i + 2 ** puissance
elif b[index] != ’0’:return None
puissance = puissance + 1index = index - 1
return i
>>> print bin2dec(’10011’)19>>> print bin2dec(’123’)None
Programmation (Chap. 5) Itérations et chaînes de caractères 42 / 98
Un str est une séquence immuable de caractères
Exercices
ProblèmeQue fait la fonction suivante ?
def find(word, letter):index = 0while index < len(word):
if word[index] == letter:return index
index = index + 1return None
retourne l’indice du premier caractère de la chaîne mot quicorrespond au caractère letter, ou None si ce caractère n’estpas présent.
Programmation (Chap. 5) Itérations et chaînes de caractères 43 / 98
Un str est une séquence immuable de caractères
Exercices
ProblèmeQue fait la fonction suivante ?
def find(word, letter):index = 0while index < len(word):
if word[index] == letter:return index
index = index + 1return None
retourne l’indice du premier caractère de la chaîne mot quicorrespond au caractère letter, ou None si ce caractère n’estpas présent.
Programmation (Chap. 5) Itérations et chaînes de caractères 43 / 98
Un str est une séquence immuable de caractères
Exercices
La fonction find est donc le contraire de l’opérateur [.], sauf qu’ellene retourne que le premier indice d’une lettre donnée.
>>> mot = ’banane’>>> find(mot, ’m’)>>> find(mot, ’a’)1>>> mot[1]’a’
Programmation (Chap. 5) Itérations et chaînes de caractères 44 / 98
Un str est une séquence immuable de caractères
Tranches de chaînesUn segment d’une chaîne est appelé une tranche (“slice”). On peutaccéder à une tranche d’une chaîne avec l’opérateur [n:m] où
n est l’indice du premier caractère de la tranche (inclus danscelle-ci)
m est l’indice du dernier caractère de la tranche (non inclus danscelle-ci)
>>> mot = ’computer’>>> mot[1:4]’omp’>>> mot[0:len(mot)]’computer’
Intuition : on peut voir l’opérateur [n:m] comme l’intervalle [n,m[(fermé – ouvert)Remarque : le nombre de caractères de la tranche est égal à m−n
(quand les indices sont positifs)
Programmation (Chap. 5) Itérations et chaînes de caractères 45 / 98
Un str est une séquence immuable de caractères
Tranches de chaînes
Si le premier indice est omis, la tranche commence au début de lachaîne (équivalent à 0)
Si le deuxième indice est omis, la tranche se termine à la fin de lachaîne (équivalent à len(.))
>>> mot = ’computer’>>> mot[:4]’comp’>>> mot[4:]’uter’>>> mot[:-1]’compute’>>> mot[:]’computer’
Programmation (Chap. 5) Itérations et chaînes de caractères 46 / 98
Un str est une séquence immuable de caractères
Tranches de chaînes
Si le premier indice est plus grand ou égal au deuxième indice, lerésultat est une chaîne vide, représentée par deux apostrophes.
>>> mot = ’computer’>>> mot[3:3]’’
Une chaîne vide ne contient pas de caractères et a une longueur 0,mais à part ça, est une chaîne de caractères comme les autres.
Programmation (Chap. 5) Itérations et chaînes de caractères 47 / 98
Un str est une séquence immuable de caractères
Exercices
ProblèmeModifier la fonction bin2dec pour gérer également les nombresbinaires fractionnaires.
Hint : on peut utiliser find pour chercher la présence d’un point etséparer la chaîne en une tranche entière et une tranche fractionnaire.
Programmation (Chap. 5) Itérations et chaînes de caractères 48 / 98
Un str est une séquence immuable de caractères
Exercices
L’ancien code de bin2dec est placé dans une fonction annexe.
>>> def bin2dec_intPart(b):i = 0n = len(b)puissance = 0index = -1while index >= -n:
if b[index] == ’1’:i = i + 2 ** puissance
elif b[index] != ’0’:return None
puissance = puissance + 1index = index - 1
return i
>>> bin2dec_intPart(’101’)5
Programmation (Chap. 5) Itérations et chaînes de caractères 49 / 98
Un str est une séquence immuable de caractères
Exercices
On crée une deuxième fonction annexe pour gérer la partiefractionnaire
>>> def bin2dec_fracPart(b):f = 0.0n = len(b)puissance = 1while puissance <= n:
if b[puissance - 1] == ’1’:f = f + 1.0 / (2 ** puissance)
elif b[puissance - 1] != ’0’:return None
puissance = puissance + 1return f
>>> bin2dec_fracPart(’01’)0.25
Programmation (Chap. 5) Itérations et chaînes de caractères 50 / 98
Un str est une séquence immuable de caractères
Exercices
On gère le cas général.
>>> def bin2dec(b):indexDot = find(b, ’.’)if indexDot == None:
return bin2dec_intPart(b)i = bin2dec_intPart(b[:indexDot])f = bin2dec_fracPart(b[indexDot + 1:])if i != None and f != None:
return i + felse:
return None
>>> bin2dec(’101.01’)5.25>>> bin2dec(’11’)3>>> bin2dec(’.1’)0.5
Programmation (Chap. 5) Itérations et chaînes de caractères 51 / 98
Un str est une séquence immuable de caractères
Une chaîne est immuableOn peut être tenté d’utiliser l’opérateur [.] dans le membre gauched’une assignation, pour modifier un de ses caractères.
>>> greeting = ’bonjour’>>> greeting[0] = ’B’TypeError: ’str’ object does not support item assignment
Cela provoque une erreur : les chaînes sont immuables, c-à-d qu’onne peut pas modifier une chaîne existante.
Solution : créer une nouvelle chaîne et assigner celle-ci à l’anciennevariable.
>>> greeting = ’bonjour’>>> new_greeting = ’B’ + greeting[1:]>>> greeting = new_greeting>>> print greetingBonjour
Programmation (Chap. 5) Itérations et chaînes de caractères 52 / 98
Un str est une séquence immuable de caractères
Une chaîne est immuable
Remarques :
on peut se passer de la variable new_greeting
l’assignation ne modifie par la chaîne de départ (valeur) maisprécise qu’une nouvelle valeur doit être référencée (le membredroit d’une assignation étant évalué avant d’affecter la valeurobtenue)
>>> greeting = ’bonjour’>>> greeting = ’B’ + greeting[1:]>>> print greetingBonjour
’bonjour’
’Bonjour’
greeting
Programmation (Chap. 5) Itérations et chaînes de caractères 53 / 98
Boucles for
Boucles for
Programmation (Chap. 5) Itérations et chaînes de caractères 54 / 98
Boucles for
Séquences
Une chaîne est une séquence, c-à-d une collection ordonnéed’éléments (ici des caractères). Il existe d’autres types de séquences,comme les tuples, déjà brièvement introduits, ou les listes.
>>> type((1, 2))<type ’tuple’>>>> range(1, 4)[1, 2, 3]>>> type(range(1, 4))<type ’list’>
La fonction range retourne une liste d’entiers.Remarque : les listes et les tuples feront l’objet de chapitres futurs.
Programmation (Chap. 5) Itérations et chaînes de caractères 55 / 98
Boucles for
SéquencesLa fonction range retourne une suite arithmétique d’entiers et possèdela syntaxe suivante :range(start, stop, step)
start (optionnel) : premier entier de la suite (par défaut 0)stop (obligatoire) : dernier entier de la suite non-comprisstep (optionnel) : entier qui représente l’incrément ou ledécrément entre deux valeurs de la suite (par défaut 1)
>>> range(1, 5)[1, 2, 3, 4]>>> range(5)[0, 1, 2, 3, 4]>>> range(2, 8, 2)[2, 4, 6]>>> range(8, 0, -1)[8, 7, 6, 5, 4, 3, 2, 1]>>> range(stop=9, step=3)TypeError: range() takes no keyword arguments
La fonction range ne prend pas d’arguments mots-clefs : il faut doncspécifier start si on veut définir step.Programmation (Chap. 5) Itérations et chaînes de caractères 56 / 98
Boucles for
Boucles for
Une deuxième forme de structure itérative : boucle for . . .in (“pour. . . dans”) qui peut être utilisée sur une séquence.
Syntaxefor variable in séquence :
instructions
ComportementLes instructions (indentées) sont exécutées de manière répétée pourchaque élément de la séquence : à chaque itération, l’élément estaffecté à la variable
Programmation (Chap. 5) Itérations et chaînes de caractères 57 / 98
Boucles for
Boucles for
Exemples :
>>> for lettre in ’bonjour’:print lettre,
b o n j o u r>>> point = (2, 3, 5)>>> for coord in point:
print coord,
2 3 5>>> sum = 0>>> for i in range(5):
sum = sum + i
>>> sum10
Programmation (Chap. 5) Itérations et chaînes de caractères 58 / 98
Boucles for
Exercices
ProblèmeEcrire une fonction qui compte le nombre d’occurrences d’une lettredonnée dans une chaîne.
ProblèmeEcrire une fonction qui calcule la somme des nombres pairs présentsdans l’intervalle [n,m] (n ≤m).
Programmation (Chap. 5) Itérations et chaînes de caractères 59 / 98
Boucles for
Boucles for
ProblèmeEcrire une fonction qui compte le nombre d’occurrences d’une lettredonnée dans une chaîne.
>>> def count(word, letter):cnt = 0for char in word:
if char == letter:cnt = cnt + 1
return cnt
>>> count(’banane’, ’n’)2
Programmation (Chap. 5) Itérations et chaînes de caractères 60 / 98
Boucles for
Boucles for
ProblèmeEcrire une fonction qui calcule la somme des nombres pairs présentsdans l’intervalle [n,m] (n ≤m).
>>> def sum_even(n, m):if n % 2 == 1:
n = n + 1sum = 0for i in range(n, m + 1, 2):
sum = sum + ireturn sum
>>> sum_even(2, 4)6>>> sum_even(1, 5)6
Programmation (Chap. 5) Itérations et chaînes de caractères 61 / 98
Invocation de méthodes sur les chaînes
Invocation de méthodes surles chaînes
Programmation (Chap. 5) Itérations et chaînes de caractères 62 / 98
Invocation de méthodes sur les chaînes
Objets
La notion d’objet a déjà été rencontrée (par ex., objets “module” etobjets “fonctions”).
>>> import math>>> print(math)<module ’math’ from ’/Library/python2.6/lib-dynload/math.so’>>>> print(sum_even)<function sum_even at 0xead7b0>
Programmation (Chap. 5) Itérations et chaînes de caractères 63 / 98
Invocation de méthodes sur les chaînes
ObjetsC’est un peu limitatif mais, pour le moment, considérons les objetscomme des valeurs pouvant être assignées à une variable etpossédant des attributs.
Nous avons vu que la notation point permet d’accéder aux attributsdes objets, comme des variables ou des fonctions dans un module. Lafonction dir permet d’afficher la liste des attributs d’un objet.
>>> math.sin(math.pi / 2.0)1.0>>> dir(sum_even)[’__call__’, ’__class__’, ’__closure__’, ’__code__’, ’__defaults__’,’__delattr__’, ’__dict__’, ’__doc__’, ’__format__’, ’__get__’,’__getattribute__’, ’__globals__’, ’__hash__’, ’__init__’, ’__module__’,’__name__’, ’__new__’, ’__reduce__’, ’__reduce_ex__’, ’__repr__’,’__setattr__’, ’__sizeof__’, ’__str__’, ’__subclasshook__’, ’func_closure’,’func_code’, ’func_defaults’, ’func_dict’, ’func_doc’, ’func_globals’,’func_name’]>>> print sum_even.__name__sum_even
Programmation (Chap. 5) Itérations et chaînes de caractères 64 / 98
Invocation de méthodes sur les chaînes
Objets
En réalité, en Python, tout est objet. Cela implique que tout peut êtreassigné à une variable ou passé comme argument à une fonction,même une fonction ou un module.
Les entiers et les chaînes de caractères sont donc aussi des objets.
>>> myStrObject = ’bonjour’>>> dir(myStrObject)[’__add__’, ’__class__’, ’__contains__’, ’__delattr__’, ’__doc__’, ’__eq__’,...’capitalize’, ’center’, ’count’, ’decode’, ’encode’, ’endswith’, ’expandtabs’,’find’, ’format’, ’index’, ’isalnum’, ’isalpha’, ’isdigit’, ’islower’,’isspace’, ’istitle’, ’isupper’, ’join’, ’ljust’, ’lower’, ’lstrip’, ’partition’,’replace’, ’rfind’, ’rindex’, ’rjust’, ’rpartition’, ’rsplit’, ’rstrip’, ’split’,’splitlines’, ’startswith’, ’strip’, ’swapcase’, ’title’, ’translate’, ’upper’,’zfill’]
Programmation (Chap. 5) Itérations et chaînes de caractères 65 / 98
Invocation de méthodes sur les chaînes
Méthodes
Les attributs d’un objet str sont nombreux et de types divers, mais laplupart d’entre eux sont des méthodes.
>>> myStrObject = ’bonjour’>>> type(myStrObject.__doc__)<type ’str’>>>> type(myStrObject.upper)<type ’builtin_function_or_method’>>>> type(’hello’.find)<type ’builtin_function_or_method’>
Une méthode est similaire à une fonction — elle prend des argumentet retourne une valeur — mais elle s’applique sur un objet, et lasyntaxe est donc différente.
Programmation (Chap. 5) Itérations et chaînes de caractères 66 / 98
Invocation de méthodes sur les chaînes
Invocations de méthodes
Pour mettre en majuscules tous les caractères d’une chaîne, on peut
écrire une fonction qui prend la chaîne à convertir en paramètre :>>> def upper(s):
...
>>> upper(’bonjour’)’BONJOUR’
invoquer la méthode upper qui est disponible pour tous les objetsde type str
>>> s = ’bonjour’>>> s.upper()’BONJOUR’>>> ’hello’.upper()’HELLO’
Programmation (Chap. 5) Itérations et chaînes de caractères 67 / 98
Invocation de méthodes sur les chaînes
Invocations de méthodesOn parle d’appel à une fonction et on dit qu’on invoque une méthodesur un objet.
>>> upper(’bonjour’)’BONJOUR’>>> ’bonjour’.upper()’BONJOUR’
Une fonction n’est pas “invoquée” sur un objet : elle doit doncpasser toutes ses entrées en paramètres.
Une méthode est invoquée sur un objet grâce à la notation point :il n’est pas nécessaire de passer cet objet en paramètre.
A part cette différence de syntaxe, l’utilisation des méthodes estsimilaire à l’utilisation des fonctions (parenthèses obligatoires,arguments, valeur de retour, etc.).
Programmation (Chap. 5) Itérations et chaînes de caractères 68 / 98
Invocation de méthodes sur les chaînes
Exemples de méthodes sur les chaînes
>>> print ’’.islower.__doc__S.islower() -> bool
Return True if all cased characters in S are lowercase and there isat least one cased character in S, False otherwise.>>> ’hello’.islower()True>>> ’Hello’.islower()False>>> ’1’.isdigit()True>>> ’a’.isdigit()False
Programmation (Chap. 5) Itérations et chaînes de caractères 69 / 98
Invocation de méthodes sur les chaînes
Exemples de méthodes sur les chaînes
Notre fonction find devient obsolète grâce à la méthode findprédéfinie :
>>> find(’banane’,’n’)2>>> ’banane’.find(’n’)2
Programmation (Chap. 5) Itérations et chaînes de caractères 70 / 98
Invocation de méthodes sur les chaînes
Exemples de méthodes sur les chaînes
Cette méthode est plus évoluée que notre fonction find :
>>> print ’’.find.__doc__S.find(sub [,start [,end]]) -> int
Return the lowest index in S where substring sub is found,such that sub is contained within s[start:end]. Optionalarguments start and end are interpreted as in slice notation.>>> mot = ’banane’>>> mot.find(’an’)1>>> mot.find(’an’,2)3
Programmation (Chap. 5) Itérations et chaînes de caractères 71 / 98
Invocation de méthodes sur les chaînes
Exemples de méthodes sur les chaînes
Exercice : lisez la documentation de la méthode count pour écrire uneinstruction qui permette de compter le nombre de “n” du mot “banane”.Qu’est-ce qui est obligatoire et qu’est-ce qui est optionnel ? Commentle voit-on dans l’en-tête de la méthode ?
>>> print ’’.count.__doc__S.count(sub[, start[, end]]) -> int
Return the number of non-overlapping occurrences of substring sub instring S[start:end]. Optional arguments start and end are interpretedas in slice notation.
Programmation (Chap. 5) Itérations et chaînes de caractères 72 / 98
Opérateurs supplémentaires sur les chaînes
Opérateurs supplémentairessur les chaînes
Programmation (Chap. 5) Itérations et chaînes de caractères 73 / 98
Opérateurs supplémentaires sur les chaînes
Opérateur in
“in” n’est pas utilisé que dans une boucle for. C’est aussi unopérateur booléen qui retourne vrai si un élément est inclus dans uneséquence. Dans le cas des chaînes, il retourne vrai si unesous-chaîne est inclue dans une chaîne.
>>> ’a’ in ’banane’True>>> ’pepin’ in ’banane’False
Programmation (Chap. 5) Itérations et chaînes de caractères 74 / 98
Opérateurs supplémentaires sur les chaînes
Opérateur in
Par exemple, la fonction suivante affiche toutes les lettres d’un mot quiapparaissent aussi dans un deuxième mot :
>>> def in_both(word1, word2):for letter in word1:
if letter in word2:print letter,
>>> in_both(’pommes’,’oranges’)o e s
Remarque : ce code Python est très proche d’une phrase en anglais,et ce grâce aux noms des variables explicites.
“for (each) letter in (the first) word, if (the) letter (appears) in (the
second) word, print (the) letter.”
Programmation (Chap. 5) Itérations et chaînes de caractères 75 / 98
Opérateurs supplémentaires sur les chaînes
Comparaison de chaînesL’opérateur de comparaison == fonctionne sur les chaînes :
if word == ’done’:break
Les autres opérateurs de comparaison peuvent être également utiliséssur des chaînes : dans ce cas, “plus petit” ou “plus grand” signifie“précède” ou “suit” dans l’ordre alphabétique.
>>> ’avoir’ < ’etre’True>>> ’elephant’ > ’zebre’False>>> ’avoir’ < ’Etre’False
Remarque : en Python, toutes les lettres majuscules viennent avantles lettres minuscules. La méthode lower peut être utile si c’est unproblème.Programmation (Chap. 5) Itérations et chaînes de caractères 76 / 98
Opérateurs supplémentaires sur les chaînes
Comparaison de chaînes
ProblèmeEcrire une fonction qui permet de trier trois mots, quelle que soit lacasse des mots.
Programmation (Chap. 5) Itérations et chaînes de caractères 77 / 98
Opérateurs supplémentaires sur les chaînes
Comparaison de chaînes
ProblèmeEcrire une fonction qui permet de trier trois mots, quelle que soit lacasse des mots.
On commence par écrire une fonction qui permet de trier deux mots etqui gère le problème de la casse.
>>> def trie2(mot1, mot2):first = mot1second = mot2if mot1.lower() > mot2.lower():
first = mot2second = mot1
return (first, second)
>>> trie2(’Zebre’,’elephant’)(’elephant’, ’Zebre’)
Programmation (Chap. 5) Itérations et chaînes de caractères 78 / 98
Opérateurs supplémentaires sur les chaînes
Comparaison de chaînes
On peut maintenant trier trois mots sans se soucier de la casse : onles trie deux à deux.
>>> def trie3(mot1, mot2, mot3):(mot1, mot2) = trie2(mot1, mot2)# le plus grand de mot1 et mot2 est devenu mot2
(mot2, mot3) = trie2(mot2, mot3)# mot3 est le plus grand des 3 mots
(mot1, mot2) = trie2(mot1, mot2)# il se peut que (le nouveau) mot2 soit plus petit que mot1
return (mot1, mot2, mot3)>>> trie3(’a’, ’c’, ’b’)(’a’, ’b’, ’c’)>>> trie3(’c’, ’b’, ’a’)(’a’, ’b’, ’c’)
Programmation (Chap. 5) Itérations et chaînes de caractères 79 / 98
Jouer avec les mots
Jouer avec les mots
Programmation (Chap. 5) Itérations et chaînes de caractères 80 / 98
Jouer avec les mots
Jouer avec les mots
Le fichier words.txt2 (disponible sur e-learning) contient 113809 motsanglais qui sont considérés comme les mots officiels acceptés dansles mots-croisés.
C’est un fichier “plain text”, c-à-d que vous pouvez le lire dansn’importe quel éditeur de texte, mais également via Python.
Nous allons l’utiliser pour résoudre quelques problèmes sur les mots.
2Fichier disponible publiquement, fourni par Grady Ward pour le projet “Moby”Programmation (Chap. 5) Itérations et chaînes de caractères 81 / 98
Jouer avec les mots
Lire un fichier de texte, ligne par ligne.
La fonction open prend en argument le nom d’un fichier, “ouvre” lefichier (par défaut, en mode lecture : mode ’r’ (read)), et retourne unobjet fichier qui permet de le manipuler.
>>> fichier = open(’words.txt’) # cherche ce fichier dans le repertoire courant>>> print fichier<open file ’words.txt’, mode ’r’ at 0x5d660>
Programmation (Chap. 5) Itérations et chaînes de caractères 82 / 98
Jouer avec les mots
Lire un fichier de texte, ligne par ligne.La méthode readline invoquée sur un objet fichier permet de lire uneligne. Lors de la première invocation, la première ligne est retournée.Lors de l’invocation suivante de cette même méthode, la prochaineligne sera lue.
>>> fichier.readline()’aa\r\n’>>> fichier.readline()’aah\r\n’
“aa” est une sorte de lave.
la séquence \r\n représente deux caractères “blancs”3: un“retour chariot” et une nouvelle ligne, qui sépare ce mot dusuivant.
l’objet fichier retient l’endroit où l’on se trouve dans la lecture, laseconde invocation permet d’obtenir le mot suivant.
3D’autres caractères blancs : un espace ou une tabulation \tProgrammation (Chap. 5) Itérations et chaînes de caractères 83 / 98
Jouer avec les mots
Lire un fichier de texte, ligne par ligne.
La méthode strip invoquée sur un objet retourne une copie de lachaîne en retirant les caractères “blancs” qui se trouvent au début eten fin de la chaîne.
>>> line = fichier.readline()>>> line.strip()’aahed’>>> fichier.readline().strip()’aahing’
comprenez-vous la dernière instruction ? (Hint : que retournereadline ?)
puisque la valeur retournée par une méthode est un objet (toutest objet), la notation point permet d’invoquer des méthodes encascade à lire de gauche à droite : c’est une forme de lacomposition.
Programmation (Chap. 5) Itérations et chaînes de caractères 84 / 98
Jouer avec les mots
Lire un fichier de texte, ligne par ligne.
Une boucle for peut être utilisée pour lire les lignes d’un fichier detexte une à une.
>>> for line in fichier:print line.strip()
aahsaalaaliiaaliis...zymoseszymosiszymoticzymurgieszymurgy
Programmation (Chap. 5) Itérations et chaînes de caractères 85 / 98
Jouer avec les mots
ExercicesProblèmeEcrivez un script qui lit le fichier words.txt et qui n’affiche que lesmots qui ont plus de 20 caractères (sans compter les caractèresblancs).
ProblèmeEn 1939, Ernest Wright a publié une nouvelle de 50000 mots appeléeGadsby qui ne contient pas la lettre “e”. Comme “e” est la lettre la pluscommune en anglais (et dans d’autres langues), ce n’était pas unetâche facilea.Ecrivez un script qui n’affiche que les mots qui ne contiennent pas de“e” et calculez le pourcentage de ceux-ci par rapport à l’ensemble desmots du fichier words.txt.
aGeorges Perec a fait le même exercice en français, dans son livre La disparition
(1969) où il définit ce qui a disparu comme “un rond pas tout à fait clos, fini par un traithorizontal”.
Programmation (Chap. 5) Itérations et chaînes de caractères 86 / 98
Jouer avec les mots
Exercices
Solution : les mots de plus de 20 caractères :
fichier = open(’words.txt’)for line in fichier:
word = line.strip()if len(word) > 20:
print word
Programmation (Chap. 5) Itérations et chaînes de caractères 87 / 98
Jouer avec les mots
Exercices
Solution : les mots sans “e” :
def has_no_e(s):if ’e’ in s:
return Falseelse:
return True
fichier = open(’words.txt’)total = 0cnt = 0for line in fichier:
total = total + 1word = line.strip()if has_no_e(word):
print wordcnt = cnt + 1
percent = float(cnt) / total * 100.0print ’Pourcentage de mots sans e: %.2f’ % percent
Programmation (Chap. 5) Itérations et chaînes de caractères 88 / 98
Jouer avec les mots
Exercices
ProblèmeDonnez-moi un mot anglais avec 3 doubles lettres consécutives. Jevous donne un couple de mots qui sont presque candidats, mais pastout à fait. Par exemple, le mot “committee” serait parfait s’il n’y avaitpas ce “i” au milieu. Ou “Mississippi” : si on retirait les “i”, celamarcherait.
Il y a cependant au moins un mot qui possède trois pairesconsécutives de lettres. C’est peut-être le seul mot, ou il peut y enavoir 500. Pourrez-vous me dire, pour le prochain cours, quel est lemot auquel je pense ?
Programmation (Chap. 5) Itérations et chaînes de caractères 89 / 98
Erreurs fréquentes et debug
Erreurs fréquentes et debug
Programmation (Chap. 5) Itérations et chaînes de caractères 90 / 98
Erreurs fréquentes et debug
Debug par bissectionQuand la taille d’un programme augmente, il en va de même pour letravail de debug. Une manière de gagner du temps pour trouver unbug, est la méthode dite de “bissection”. Par exemple,
s’il y a 100 lignes de code, et que vous le testez ligne après ligne,cela fera 100 étapes.
essayez plutôt de couper le problème en deux. Inspectez aumilieu du programme une valeur que vous pouvez tester (ajoutezun print ou quelque chose qui a un effet vérifiable).
si le test au milieu est incorrect, le problème doit se trouver dansla première partie du programme. S’il est correct, il doit se trouverdans la seconde partie.
répétez la procédure jusqu’à la localisation précise du problème.
en pratique, il n’est pas toujours facile (ou possible) de tester le“milieu” du programme. Dans ce cas, choisissez des endroits oùajouter un test est facile.
Programmation (Chap. 5) Itérations et chaînes de caractères 91 / 98
Erreurs fréquentes et debug
Erreurs d’indicesQuand on utilise les indices pour traverser une séquence, les erreursd’indices sont fréquentes (indice de fin ou de début).
Voici une fonction qui est supposée comparer deux mots et retournerTrue si l’un des mots est l’inverse de l’autre, mais elle contient deuxerreurs :
def is_reverse(word1, word2):if len(word1) != len(word2):
return Falsei = 0j = len(word2)while j > 0:
if word1[i] != word2[j]:return False
i = i + 1j = j - 1
return True
Programmation (Chap. 5) Itérations et chaînes de caractères 92 / 98
Erreurs fréquentes et debug
Erreurs d’indices
le premier if vérifie que les 2 mots ont la même longeur, si cen’est pas le cas, ils ne peuvent être inverses l’un de l’autre
i et j sont des indices : i traverse word1 de g. à dr. et j traverseword2 de dr. à g.
le deuxième if vérifie que les lettres “miroir” sont équivalentes, sice n’est pas le cas, les mots ne peuvent être inverses l’un del’autre
si on traverse toute la boucle et que toutes les lettrescorrespondent, on peut retourner True
Programmation (Chap. 5) Itérations et chaînes de caractères 93 / 98
Erreurs fréquentes et debug
Erreurs d’indices
Si on teste la fonction avec les mots “pots” et “stop”, on s’attend àobtenir True, mais on obtient une erreur d’index :
>>> is_reverse(’pots’, ’stop’)...
File "<pyshell#211>", line 7, in is_reverseif word1[i] != word2[j]:
IndexError: string index out of range
Pour déboguer les erreurs d’indices, un reflexe utile est d’afficher lesindices.
...while j > 0:
print ’i:’, i, ’j:’, j...
Programmation (Chap. 5) Itérations et chaînes de caractères 94 / 98
Erreurs fréquentes et debug
Erreurs d’indices
On observe que lors de la première itération, l’erreur est déjàprovoquée. Il s’agit de l’indice de j qui est plus grand quelen(’stop’)-1 :
>>> is_reverse(’pots’, ’stop’)i: 0 j: 4...
if word1[i] != word2[j]:IndexError: string index out of range
Correction :
def is_reverse(word1, word2):...
j = len(word2) - 1...
Programmation (Chap. 5) Itérations et chaînes de caractères 95 / 98
Erreurs fréquentes et debug
Erreurs d’indices
On obtient :
>>> is_reverse(’pots’, ’stop’)i: 0 j: 3i: 1 j: 2i: 2 j: 1True
La réponse est bonne mais la boucle itère 3 fois : est-ce normal ? Lafonction est-elle correcte ?
Programmation (Chap. 5) Itérations et chaînes de caractères 96 / 98
Glossaire
Glossaire
Programmation (Chap. 5) Itérations et chaînes de caractères 97 / 98
Glossaire
Glossaire
incrémenter : mettre à jour une variable en augmentant sa valeur (souvent de 1).
décrémenter : mettre à jour une variable en diminuant sa valeur (souvent de −1).
itération : (ou boucle) exécution répétée d’un ensemble d’instructions.
boucle infinie : une boucle dont la condition de fin n’est jamais satisfaite.
objet : quelque chose qu’une variable peut référer. Pour le moment, un objet est une“valeur” qui possède des attributs et des méthodes.
séquence : ensemble ordonné, c-à-d un ensemble de valeurs où chaque valeur estidentifiée par un index entier.
élément : (item) une des valeurs d’une séquence.
tranche : (slice) partie d’une chaîne spécifiée par un intervalle d’indices.
chaîne vide : chaîne sans caractère et de longueur 0, représentée par deux apostrophes.
immuable : propriété d’une séquence dont les éléments ne peuvent être assignés.
méthode : fonction qui est associée à un objet et qui est invoquée en utilisant la notationpoint.
Programmation (Chap. 5) Itérations et chaînes de caractères 98 / 98
Chapitre 6. Listes
Contenu du chapitre1 Une liste est une séquence d’éléments
2 Listes et opérateurs
3 Méthodes sur les listes
4 Arguments de la ligne de commande
5 Opérations fréquentes sur les listes
6 Listes et chaînes
7 Objets et valeurs
8 Erreurs fréquentes et debug
9 Glossaire
Programmation (Chap. 6) Listes 1 / 70
Une liste est une séquence d’éléments
Une liste est une séquence
d’éléments
Programmation (Chap. 6) Listes 2 / 70
Une liste est une séquence d’éléments
Une liste est une séquence d’élements
Comme une chaîne de caractères, une liste est une séquence de
valeurs, mais :
dans une chaîne les valeurs sont des caractères, dans une liste
elles peuvent être de n’importe quel type (et pas forcément le
même pour tous les éléments).
une chaîne est une séquence immuable, une liste est une
séquence que l’on peut modifier.
une liste est définie en spécifiant ses éléments, séparés par des
virgules, entre crochets (par ex. : [1, 2, 3])
Programmation (Chap. 6) Listes 3 / 70
Une liste est une séquence d’éléments
Création de listes
Remarques :
une liste peut être vide
une liste peut contenir des valeurs de types différents
une liste peut contenir des listes, des tuples, ou n’importe quelle
autre type de valeur
la fonction len peut être appliquée sur une liste
>>> empty = []>>> type(empty)<type ’list’>>>> data = [’text’, 2.0, 1, [2, 3]]>>> print data[’text’, 2.0, 1, [2, 3]]>>> len(data)4
Programmation (Chap. 6) Listes 4 / 70
Une liste est une séquence d’éléments
Accéder et modifier les éléments d’une liste.
On peut accéder aux éléments d’une liste grâce à l’opérateur [.]. Les
indices fonctionnent de la même manière que pour les chaînes de
caractères.
>>> data = [’text’, 2.0, 1, [2, 3]]>>> print data[0]text>>> print data[-1][2, 3]
Programmation (Chap. 6) Listes 5 / 70
Une liste est une séquence d’éléments
Accéder et modifier les éléments d’une liste.
On peut représenter les listes par un diagramme d’état qui illustre les
relations entre les indices et les éléments.
>>> empty = []>>> numbers = [1, 3, 6]
empty
numbers 0 1
1 3
2 6
Programmation (Chap. 6) Listes 6 / 70
Une liste est une séquence d’éléments
Accéder et modifier les éléments d’une liste.
>>> data = [’text’, 2.0, 1, [2, 3]]
0 ’text’
1 2.0
2 1
3
data
2
1 3
0
Programmation (Chap. 6) Listes 7 / 70
Une liste est une séquence d’éléments
Accéder et modifier les éléments d’une liste.
Contrairement aux chaînes qui sont immuables, on peut modifier les
éléments d’une liste.
>>> mot = ’bonjour’>>> numbers = [1, 3, 6]>>> mot[0] = ’B’
Traceback (most recent call last):File "<pyshell#36>", line 1, in <module>
mot[0] = ’B’TypeError: ’str’ object does not support item assignment>>> numbers[0] = 12>>> print numbers[12, 3, 6]
numbers 0 1
1 3
2 6
12
Programmation (Chap. 6) Listes 8 / 70
Une liste est une séquence d’éléments
Boucles for
Une liste est une séquence : peut être utilisée dans une boucle for.
data = [’text’, 2.0, 1, [2, 3]]
for item in data:print item, type(item)
text <type ’str’>2.0 <type ’float’>1 <type ’int’>[2, 3] <type ’list’>
Remarque : nous l’avions déjà utilisé avec la fonction range qui
retourne une liste d’entiers.
Programmation (Chap. 6) Listes 9 / 70
Une liste est une séquence d’éléments
Boucles for
Si on a besoin d’accéder aux indices de la liste, une manière de faire
est d’utiliser range et len :
for i in range(len(numbers)):numbers[i] = numbers[i] * 2
Une boucle for sur une liste vide n’exécute jamais son corps.
for x in []:print ’This never happens.’
Programmation (Chap. 6) Listes 10 / 70
Listes et opérateurs
Listes et opérateurs
Programmation (Chap. 6) Listes 11 / 70
Listes et opérateurs
Listes et opérateurs
Comme pour les chaînes, l’opérateur + concatène deux listes,
l’opérateur * répète une liste un certain nombre de fois.
>>> a = [1, 2, 3]>>> b = [4, 5, 6]>>> c = a + b>>> print c[1, 2, 3, 4, 5, 6]>>> [0] * 4[0, 0, 0, 0]>>> a * 3[1, 2, 3, 1, 2, 3, 1, 2, 3]
Programmation (Chap. 6) Listes 12 / 70
Listes et opérateurs
Opérateur in
Comme pour toutes les séquences, l’opérateur in est applicable sur
une liste.
>>> cheeses = [’Cheddar’, ’Edam’, ’Gouda’]>>> ’Edam’ in cheesesTrue>>> ’Brie’ in cheesesFalse
Programmation (Chap. 6) Listes 13 / 70
Listes et opérateurs
Tranches de listes
L’opérateur [:] fonctionne aussi sur les listes. Il peut être utilisé dans
le membre gauche d’une assignation.
>>> t = range(10)>>> print t[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]>>> t[1:3][1, 2]>>> t[:4][0, 1, 2, 3]>>> t[3:][3, 4, 5, 6, 7, 8, 9]>>> t[1:3] = [34,26]>>> print t[0, 34, 26, 3, 4, 5, 6, 7, 8, 9]
Programmation (Chap. 6) Listes 14 / 70
Listes et opérateurs
Copie d’une liste
Comme les listes sont mutables, il est parfois utile de faire une copie
avant de les modifier. On peut créer une copie avec une “tranche
complète”.
>>> copy = t[:]>>> t[4:6] = [0] * 2>>> print t, copy[0, 34, 26, 3, 0, 0, 6, 7, 8, 9] [0, 34, 26, 3, 4, 5, 6, 7, 8, 9]
Programmation (Chap. 6) Listes 15 / 70
Listes et opérateurs
Exercice
Problème
Construire la liste suivante :
[1,3,0,0,9,11,13,15,1,3,0,0,9,11,13,15]
Programmation (Chap. 6) Listes 16 / 70
Listes et opérateurs
Exercice
Problème
Construire la liste suivante :
[1,3,0,0,9,11,13,15,1,3,0,0,9,11,13,15]
>>> liste = range(1,16,2)>>> print liste[1, 3, 5, 7, 9, 11, 13, 15]>>> liste[2:4] = [0] * 2>>> print liste[1, 3, 0, 0, 9, 11, 13, 15]>>> liste = liste * 2>>> print liste[1, 3, 0, 0, 9, 11, 13, 15, 1, 3, 0, 0, 9, 11, 13, 15]
Programmation (Chap. 6) Listes 17 / 70
Méthodes sur les listes
Méthodes sur les listes
Programmation (Chap. 6) Listes 18 / 70
Méthodes sur les listes
Méthodes sur les listes
Les objets “liste” possèdent une série de méthodes utiles.
>>> dir(list)[...’append’, ’count’, ’extend’, ’index’, ’insert’, ’pop’, ’remove’,’reverse’, ’sort’]
Programmation (Chap. 6) Listes 19 / 70
Méthodes sur les listes
Méthodes sur les listes
La méthode append ajoute un nouvel élément à la fin de la liste.
>>> help(list.append)Help on method_descriptor:
append(...)L.append(object) -- append object to end
>>> t = [’a’, ’b’, ’c’]>>> t.append(’d’)>>> print t[’a’, ’b’, ’c’, ’d’]
Programmation (Chap. 6) Listes 20 / 70
Méthodes sur les listes
Méthodes sur les listes
La méthode extend prend une liste en argument et ajoute tous ces
éléments en fin de la liste.
>>> help(list.extend)Help on method_descriptor:
extend(...)L.extend(iterable) -- extend list by appending elements from the iterable
>>> t1 = [’a’, ’b’, ’c’]>>> t2 = [’d’, ’e’]>>> t1.extend(t2)>>> print t1[’a’, ’b’, ’c’, ’d’, ’e’]
Programmation (Chap. 6) Listes 21 / 70
Méthodes sur les listes
Méthodes sur les listes
La méthode count compte toutes les occurrences d’une valeur passée
en argument.
>>> help(list.count)Help on method_descriptor:
count(...)L.count(value) -> integer -- return number of occurrences of value
>>> t = [’a’, ’b’, ’c’, ’a’]>>> t.count(’a’)2
Programmation (Chap. 6) Listes 22 / 70
Méthodes sur les listes
Méthodes sur les listes
La méthode index retourne l’indice de la première occurrence d’une
valeur donnée (des arguments optionnels permettent de limiter la
recherche à un intervalle d’indices).
>>> help(list.index)Help on method_descriptor:
index(...)L.index(value, [start, [stop]]) -> integer -- return first index of value.Raises ValueError if the value is not present.
>>> t = [’a’, ’b’, ’c’, ’a’]>>> t.index(’a’)0>>> t.index(’z’)
Traceback (most recent call last):File "<pyshell#78>", line 1, in <module>
t.index(’z’)ValueError: list.index(x): x not in list>>> t.index(’a’,1)3
Programmation (Chap. 6) Listes 23 / 70
Méthodes sur les listes
Méthodes sur les listes
La méthode sort trie les éléments d’une liste. Tous ses arguments
sont optionnels : reverse permet de trier par ordre décroissant, les
deux autres arguments seront détaillés plus tard.
>>> help(list.sort)Help on method_descriptor:
sort(...)L.sort(cmp=None, key=None, reverse=False) -- stable sort *IN PLACE*;cmp(x, y) -> -1, 0, 1
>>> t = [’d’, ’c’, ’e’, ’b’, ’a’]>>> t.sort()>>> print t[’a’, ’b’, ’c’, ’d’, ’e’]>>> t.sort(reverse=True)>>> print t[’e’, ’d’, ’c’, ’b’, ’a’]
Programmation (Chap. 6) Listes 24 / 70
Méthodes sur les listes
Méthodes sur les listes
La méthode insert permet d’insérer un élément à un endroit donné
de la liste.
>>> help(list.insert)Help on method_descriptor:
insert(...)L.insert(index, object) -- insert object before index
>>> t = [’a’, ’b’, ’c’]>>> t.insert(0,’z’)>>> print t[’z’, ’a’, ’b’, ’c’]>>> t.insert(2,’x’)>>> print t[’z’, ’a’, ’x’, ’b’, ’c’]
Programmation (Chap. 6) Listes 25 / 70
Méthodes sur les listes
Méthodes sur les listes
La méthode remove supprime la première occurence d’une valeur
donnée.
>>> help(list.remove)Help on method_descriptor:
remove(...)L.remove(value) -- remove first occurrence of value.Raises ValueError if the value is not present.
>>> t = [’a’, ’b’, ’c’]>>> t.remove(’z’)
Traceback (most recent call last):File "<pyshell#100>", line 1, in <module>
t.remove(’z’)ValueError: list.remove(x): x not in list>>> t.remove(’b’)>>> print t[’a’, ’c’]
Programmation (Chap. 6) Listes 26 / 70
Méthodes sur les listes
Méthodes sur les listes
La méthode pop supprime le dernier élément de la liste et retourne
l’élément supprimé. Si l’argument optionnel est défini, supprime et
retourne l’élément situé à l’indice donné.
>>> help(list.pop)Help on method_descriptor:
pop(...)L.pop([index]) -> item -- remove and return item at index (default last).Raises IndexError if list is empty or index is out of range.
>>> t = [’a’, ’b’, ’c’, ’d’]>>> deleted = t.pop()>>> print deletedd>>> print t[’a’, ’b’, ’c’]>>> t.pop(1)’b’>>> print t[’a’, ’c’]
Programmation (Chap. 6) Listes 27 / 70
Méthodes sur les listes
Supprimer les éléments d’une liste
Outre les méthodes remove et pop, l’opérateur del peut également
être utilisé pour supprimer un élément à partir d’un indice (ou d’une
tranche d’indices).
>>> t = [’a’, ’b’, ’c’, ’d’]>>> x = t.pop(1)>>> print xb>>> print t[’a’, ’c’, ’d’]>>> x = t.remove(’c’)>>> print xNone>>> print t[’a’, ’d’]>>> del t[1]>>> print t[’a’]>>> t = [’a’, ’b’, ’c’, ’d’, ’e’, ’f’]>>> del t[1:5]>>> print t[’a’, ’f’]
Programmation (Chap. 6) Listes 28 / 70
Méthodes sur les listes
Exercice
Problème
Ecrire un script qui demande à l’utilisateur le nom d’un fichier de texte
(.txt), puis affiche les mots contenus dans le fichier par ordre
alphabétique (grâce à une liste). Un même mot ne peut pas être
affiché deux fois. Tous les caractères qui ne sont pas des lettres ou
des chiffres seront supprimés dans la création de la liste de mots.
Hints :
la méthode isalnum invoquée sur une chaîne retourne vrai ssi
tous les caractères de la chaîne sont des lettres ou des chiffres.
la méthode split invoquée sur une chaîne retourne une liste des
“mots” de la chaîne (c-à-d les sous-chaînes séparées par des
caractères blancs1). On peut spécifier d’autres séparateurs que
des blancs avec le paramètre optionnel sep.
1Espaces, retours à la ligne, tabulations, etc.
Programmation (Chap. 6) Listes 29 / 70
Méthodes sur les listes
Exercice
>>> help(str.split)Help on method_descriptor:
split(...)S.split([sep [,maxsplit]]) -> list of strings
Return a list of the words in the string S, using sep as thedelimiter string. If maxsplit is given, at most maxsplitsplits are done. If sep is not specified or is None, anywhitespace string is a separator and empty strings are removedfrom the result.
>>> ’Un deux trois’.split()[’Un’, ’deux’, ’trois’]>>> ’000-123456-47’.split(’-’)[’000’, ’123456’, ’47’]
Programmation (Chap. 6) Listes 30 / 70
Méthodes sur les listes
Exercice
On définit une fonction keepAlnum qui retourne la chaîne passée en
argument, en lui ayant retiré tous les caractères qui ne sont pas des
lettres ou des chiffres.
def keepAlnum(s):res = ’’for letter in s:
if letter.isalnum():res = res + letter
return res
>>> keepAlnum(’he23.?56th’)’he2356th’
Programmation (Chap. 6) Listes 31 / 70
Méthodes sur les listes
Exercice
Solution :
filename = raw_input(’Nom du fichier: ’)file = open(filename)
wordsList = []for line in file:
for word in line.split():cleanWord = keepAlnum(word)if not cleanWord in wordsList:
wordsList.append(cleanWord)
wordsList.sort()print wordsList
Programmation (Chap. 6) Listes 32 / 70
Arguments de la ligne de commande
Arguments de la ligne de
commande
Programmation (Chap. 6) Listes 33 / 70
Arguments de la ligne de commande
Arguments de la ligne de commande
La fonction raw_input permet d’obtenir des entrées de l’utilisateur.
Un autre moyen (rapide et donc souvent apprécié à l’utilisation)
d’obtenir des entrées de l’utilisateur, est de récupérer les “arguments
de la ligne de commande”.
Exemples :
la commande cd myDir vous permet de changer de répertoire
dans une console. Le nom du répertoire (myDir) est un argument
de la commande cd.
la commande python myscript.py vous permet d’interpréter le
script myscript.py. Le nom du script est un argument de la
commande python qui lance l’interpréteur.
Programmation (Chap. 6) Listes 34 / 70
Arguments de la ligne de commande
Arguments de la ligne de commande
Tous les argments passés après la commande python (et séparés par
des espaces2) sont disponibles via l’attribut argv du module sys. La
valeur argv est une liste qui contient ces arguments.
Fichier args.py :
import sysprint sys.argv
La commande “python args.py” produit :
[’args.py’]
La commande “python args.py un deux trois "x + y"” produit :
[’args.py’, ’un’, ’deux’, ’trois’, ’x + y’]
2Pour passer un argument qui contient des espaces, on peut le mettre entre
apostrophes.
Programmation (Chap. 6) Listes 35 / 70
Arguments de la ligne de commande
Arguments de la ligne de commande
Amélioration du script qui trie les mots d’un fichier :
. . .
import sys
filename = ’’if len(sys.argv) < 2:
filename = raw_input(’Nom du fichier: ’)else:
filename = sys.argv[1]
file = open(filename)
. . .
Programmation (Chap. 6) Listes 36 / 70
Opérations fréquentes sur les listes
Opérations fréquentes sur les
listes
Programmation (Chap. 6) Listes 37 / 70
Opérations fréquentes sur les listes
Opérations fréquentes sur les listes
Pour additionner tous les nombres d’une liste, on peut utiliser une
boucle :
def add_all(t):total = 0for x in t:
total += xreturn total
>>> t = range(1,11)>>> add_all(t)55
Remarques :
total += x est équivalent à total = total + x
une variable comme total qui accumule progressivement la
somme des éléments est appelée un accumulateur
Programmation (Chap. 6) Listes 38 / 70
Opérations fréquentes sur les listes
Opérations fréquentes sur les listes
Additionner les éléments d’une liste est une opération fréquente.
Python offre une fonction qui permet également de le faire.
>>> t = [1, 2, 3]>>> sum(t)6
Une opération qui combine une séquence d’éléments en une seule
valeur est appelée une réduction : séquence→ valeur.
Programmation (Chap. 6) Listes 39 / 70
Opérations fréquentes sur les listes
Opérations fréquentes sur les listes
Une autre opération fréquente : traverser une séquence en en
construisant une nouvelle. Par exemple, la fonction suivante prend
une liste de chaînes en argument et retourne une liste qui ‘capitalise”
les mots de la liste de départ3.
def capitalize_all(t):res = []for s in t:
res.append(s.capitalize())return res
>>> liste = [’hello’, ’bonjour’, ’dag’]>>> capitalize_all(liste)[’Hello’, ’Bonjour’, ’Dag’]
3On utilise la méthode capitalize sur chaque chaîne : elle met la première
lettre en majuscule.
Programmation (Chap. 6) Listes 40 / 70
Opérations fréquentes sur les listes
Opérations fréquentes sur les listes
def capitalize_all(t):res = []for s in t:
res.append(s.capitalize())return res
res est initialisé avec une liste vide; à chaque itération de la
boucle, on ajoute le nouvel élément : res est une sorte
d’accumulateur.
Une opération qui applique une fonction (ici, capitalize) sur
chaque élément d’une séquence est appelée un mapping.
Programmation (Chap. 6) Listes 41 / 70
Opérations fréquentes sur les listes
Opérations fréquentes sur les listes
Une autre opération fréquente : sélectionner certains éléments d’une
séquence et retourner une sous-séquence. Par exemple, la fonction
suivante ne retourne que les chaînes d’une liste qui sont en
majuscules.
def only_upper(t):res = []for s in t:
if s.isupper():res.append(s)
return res
>>> liste = [’Hello’, ’bonjour’, ’DAG’]>>> only_upper(liste)[’DAG’]
une opération qui sélectionne certains éléments et en rejette
d’autres est appelée un filtre.
Programmation (Chap. 6) Listes 42 / 70
Opérations fréquentes sur les listes
Opérations fréquentes sur les listes
Problème
Ecrire une fonction qui prend une liste de nombre entiers en
paramètres et qui retourne la somme cumulative, c-à-d une nouvelle
liste telle que l’élément d’indice i soit la somme des i +1 premiers
éléments.
Par exemple, la somme cumulative de [1,2,3] est [1,3,6].
Programmation (Chap. 6) Listes 43 / 70
Opérations fréquentes sur les listes
Opérations fréquentes sur les listes
Solution :
def cum_sum(t):res = []sum = 0for x in t:
sum += xres.append(sum)
return res
>>> cum_sum([1,2,3])[1, 3, 6]>>> cum_sum(range(10))[0, 1, 3, 6, 10, 15, 21, 28, 36, 45]
Programmation (Chap. 6) Listes 44 / 70
Opérations fréquentes sur les listes
Opérations fréquentes sur les listes
La plupart des opérations sur des séquences peuvent être exprimées
comme une combinaison de mappings, de filtres et de réductions.
Python possède des fonctions pour réaliser directement ces 3
opérations sur une séquence : map, filter et reduce.
Programmation (Chap. 6) Listes 45 / 70
Opérations fréquentes sur les listes
Opérations fréquentes sur les listes
Mapping : appliquer une action (fonction ou méthode) sur chaque
élément d’une séquence. Les éléments modifiés sont retournés sous
forme d’une liste.
Fonction map(function, sequence) -> list
>>> import math>>> map(math.sqrt,[2.0, 4.0, 6.0, 100.0])[1.4142135623730951, 2.0, 2.4494897427831779, 10.0]
>>> def double(lettre):return lettre + lettre
>>> map(double, ’Hello’)[’HH’, ’ee’, ’ll’, ’ll’, ’oo’]>>> map(str.upper, ’hello’)[’H’, ’E’, ’L’, ’L’, ’O’]
Programmation (Chap. 6) Listes 46 / 70
Opérations fréquentes sur les listes
Opérations fréquentes sur les listes
Filtrer : sélectionner les éléments qui satisfont un certain critère
(fonction ou méthode booléenne). Les éléments selectionnés sont
retournés sous forme d’une chaîne si la séquence est une chaîne, d’un
tuple si la séquence est un tuple, ou d’une liste dans les autres cas.
Fonction filter(function, sequence) -> list, tuple or string
>>> filter(str.isalpha, ’r2d2’)’rd’>>> def is_even(x):
return x % 2 == 0
>>> filter(is_even, [1, 2, 4, 5, 7, 10, 11])[2, 4, 10]
Programmation (Chap. 6) Listes 47 / 70
Opérations fréquentes sur les listes
Opérations fréquentes sur les listes
Réduire : appliquer successivement une fonction sur deux éléments
d’une séquence, de gauche à droite, pour réduire la séquence à une
seule valeur.
Fonction reduce(function, sequence) -> value
>>> def f(x,y):return x * y
>>> reduce(f, range(1,7))720>>> math.factorial(6)720
Remarque : on verra plus tard comment définir des “mini” fonctions
comme f ci-dessus, directement là où on en a besoin (fonctions
lambda) : la réduction s’écrira alors en une ligne.
Programmation (Chap. 6) Listes 48 / 70
Listes et chaînes
Listes et chaînes
Programmation (Chap. 6) Listes 49 / 70
Listes et chaînes
Listes et chaînes
Un str est une séquence de caractères et une liste est une séquence
de valeurs, mais une liste de caractères n’est pas la même chose
qu’une chaîne.
Pour convertir une chaîne en liste, on peut utiliser list.
>>> s = ’hello’>>> t = list(s)>>> print t[’h’, ’e’, ’l’, ’l’, ’o’]
Comme list est le nom d’une fonction, on ne peut pas l’utiliser
comme nom de variable (mais liste peut l’être).
En général, on évite d’utiliser la lettre l car elle ressemble trop au
nombre 1, c’est pourquoi t est souvent utilisé dans les exemples.
Programmation (Chap. 6) Listes 50 / 70
Listes et chaînes
Listes et chaînes
La fonction list sépare une chaîne en caractères individuellement.
Nous avons vu la méthode split qui permet de séparer une chaîne
en mots ou en sous-chaînes.
La méthode join fait le contraire de split. C’est une méthode sur les
chaînes : on l’invoque sur le délimiteur et on passe la liste en
paramètre (elle peut être utile après un map).
>>> s = ’to be or not to be’>>> t = s.split()>>> print t[’to’, ’be’, ’or’, ’not’, ’to’, ’be’]>>> s = ’spam*spam*spam’>>> delimiter = ’*’>>> s.split(delimiter)[’spam’, ’spam’, ’spam’]>>> delimiter = ’ ’>>> delimiter.join(t)’to be or not to be’>>> ’’.join(t)’tobeornottobe’
Programmation (Chap. 6) Listes 51 / 70
Objets et valeurs
Objets et valeurs
Programmation (Chap. 6) Listes 52 / 70
Objets et valeurs
Objets et valeurs
Si on exécute les assignations suivantes, on sait que a et b réfèrent à
une chaîne, mais on ne sait pas si ces variables réfèrent la même
chaîne.
>>> a = ’banana’>>> b = ’banana’
Il y a deux possibilités :
a ’banana’
b ’banana’
a
b
’banana’
Dans un cas, a et b réfèrent deux différents objets qui ont la même
valeur. Dans l’autre, elles réfèrent au même objet.
Programmation (Chap. 6) Listes 53 / 70
Objets et valeurs
Objets et valeurs
L’opérateur is permet de tester si deux variables réfèrent le même
objet.
>>> a = ’banana’>>> b = ’banana’>>> a is bTrue
Donc, dans ce cas, Python crée un seul objet “chaîne” et les deux
variables réfère à cet objet.
a
b
’banana’
Programmation (Chap. 6) Listes 54 / 70
Objets et valeurs
Objets et valeurs
Par contre, dans le cas de deux listes, deux objets sont créés.
>>> a = [1, 2, 3]>>> b = [1, 2, 3]>>> a is bFalse
[1, 2, 3]
b [1, 2, 3]
a
on dit que les deux listes sont equivalentes, car elles ont les
mêmes éléments, mais elles ne sont pas identiques, car elle ne
sont pas le même objet.
si deux objets sont identiques, ils sont aussi équivalents, mais le
contraire n’est pas toujours vrai.
Programmation (Chap. 6) Listes 55 / 70
Objets et valeurs
Objets et valeurs
Jusqu’à présent, on a utilisé les mots “objet” et “valeur” de manière
interchangeable, mais il est plus précis de dire qu’un objet possède
une valeur et un type.
De plus, un objet a accès aux attributs et méthodes qui sont définis en
fonction de son type.
Si on exécute a = [1, 2, 3] :
la variable a réfère à un objet (de type liste),
la valeur de l’objet est une séquence particulière d’éléments,
si un autre objet “liste” a les mêmes éléments, on dira qu’il a la
même valeur,
on peut invoquer sur a toutes les méthodes définies pour les
objets de type “liste”.
Programmation (Chap. 6) Listes 56 / 70
Objets et valeurs
Objets et valeurs
Pourquoi deux comportements différents en créant deux chaînes ou
deux listes ?
>>> a = ’banana’>>> b = ’banana’>>> c = [1, 2, 3]>>> d = [1, 2, 3]>>> a is bTrue>>> c is dFalse
si on modifie par après une des deux listes, l’autre n’est pas
modifiée : elles ont “pour le moment” la même valeur, mais elles
peuvent évoluer de manière indépendante, puisqu’elles sont des
objets différents.
alors, pourquoi n’est-ce pas un problème dans le cas des
chaînes ?
Car elles sont immuables ! (De plus, le fait de n’avoir
qu’un seul objet économise de l’espace mémoire.)
Programmation (Chap. 6) Listes 57 / 70
Objets et valeurs
Objets et valeurs
Pourquoi deux comportements différents en créant deux chaînes ou
deux listes ?
>>> a = ’banana’>>> b = ’banana’>>> c = [1, 2, 3]>>> d = [1, 2, 3]>>> a is bTrue>>> c is dFalse
si on modifie par après une des deux listes, l’autre n’est pas
modifiée : elles ont “pour le moment” la même valeur, mais elles
peuvent évoluer de manière indépendante, puisqu’elles sont des
objets différents.
alors, pourquoi n’est-ce pas un problème dans le cas des
chaînes ?
Car elles sont immuables ! (De plus, le fait de n’avoir
qu’un seul objet économise de l’espace mémoire.)
Programmation (Chap. 6) Listes 57 / 70
Objets et valeurs
Objets et valeurs
Pourquoi deux comportements différents en créant deux chaînes ou
deux listes ?
>>> a = ’banana’>>> b = ’banana’>>> c = [1, 2, 3]>>> d = [1, 2, 3]>>> a is bTrue>>> c is dFalse
si on modifie par après une des deux listes, l’autre n’est pas
modifiée : elles ont “pour le moment” la même valeur, mais elles
peuvent évoluer de manière indépendante, puisqu’elles sont des
objets différents.
alors, pourquoi n’est-ce pas un problème dans le cas des
chaînes ? Car elles sont immuables ! (De plus, le fait de n’avoir
qu’un seul objet économise de l’espace mémoire.)
Programmation (Chap. 6) Listes 57 / 70
Objets et valeurs
Alias
Cependant, dans certains cas, on veut que deux variables réfèrent le
même objet (même s’il est mutable).
On appelle cela un alias et on peut le faire en assignant la première
variable à la seconde.
>>> a = [1, 2, 3]>>> b = a>>> a is bTrue
a
b[1, 2, 3]
Programmation (Chap. 6) Listes 58 / 70
Objets et valeurs
Alias
Modifier un objet qui est référé par plusieurs variables est visible
depuis l’ensemble de celles-ci.
>>> a = [1, 2, 3]>>> b = [1, 2, 3]>>> c = b>>> print a, b, c[1, 2, 3] [1, 2, 3] [1, 2, 3]>>> a is b, a is c, b is c(False, False, True)>>> a[0] = 4>>> print a, b, c[4, 2, 3] [1, 2, 3] [1, 2, 3]>>> b[0] = 5>>> print a, b, c[4, 2, 3] [5, 2, 3] [5, 2, 3]
Exercice : dessiner le diagramme d’états des variables a, b et c
Programmation (Chap. 6) Listes 59 / 70
Objets et valeurs
Objets mutables en arguments
Quand on passe une liste en argument d’une fonction, la fonction
reçoit une référence vers la liste : le paramètre de la liste est un alias
et les modifications de la liste sont visibles en dehors de la fonction.
>>> def delete_head(t):del t[0]
>>> letters = [’a’, ’b’, ’c’]>>> delete_head(letters)>>> print letters[’b’, ’c’]
__main__
delete_head
0 ’a’
1 ’b’
2 ’c’t
letters
Programmation (Chap. 6) Listes 60 / 70
Objets et valeurs
Objets mutables en arguments
Il est important de faire la distinction entre ce qui modifie une liste
(comme la méthode append) et ce qui retourne une nouvelle liste
(comme l’opérateur +).
>>> t1 = [1, 2]>>> t2 = t1.append(3)>>> print t1, t2[1, 2, 3] None>>> t3 = t1 + [3]>>> print t1, t3[1, 2, 3] [1, 2, 3, 3]
Programmation (Chap. 6) Listes 61 / 70
Objets et valeurs
Objets mutables en arguments
Cette distinction est importante quand on écrit une fonction qui doit
modifier un liste passée en argument. Par exemple, l’opérateur [:]retourne une nouvelle liste (tranche). La fonction suivante, supposée
supprimer le premier élément de la liste, ne fonctionnera pas :
>>> def delete_head(t):t = t[1:]
>>> letters = [’a’, ’b’, ’c’]>>> delete_head(letters)>>> print letters[’a’, ’b’, ’c’]
l’opérateur crée une nouvelle liste et t réfère maintenant cette
nouvelle liste
le fait d’avoir changé la référence de t (intuition : la flêche sur les
diagrammes d’état), ne signifie pas qu’on a touché à celle de
letters (qui réfère toujours la liste de départ)
Programmation (Chap. 6) Listes 62 / 70
Objets et valeurs
Objets mutables en arguments
Une solution : retourner la nouvelle liste.
>>> def delete_head(t):return t[1:]
>>> letters = [’a’, ’b’, ’c’]>>> letters = delete_head(letters)>>> print letters[’b’, ’c’]
Exercice : dessiner les diagrammes qui illustrent les deux versions de
la fonction delete_head et leurs appels.
Programmation (Chap. 6) Listes 63 / 70
Erreurs fréquentes et debug
Erreurs fréquentes et debug
Programmation (Chap. 6) Listes 64 / 70
Erreurs fréquentes et debug
Erreurs fréquentes et debug
Une utilisation non rigoureuse des listes (ou d’autres objets mutables)
peut mener à des problèmes difficiles à déboguer.
la plupart des méthodes sur les listes modifient celles-ci (et
retournent None). C’est l’inverse pour les méthodes sur les
chaînes (qui ne pourraient pas modifier l’argument) : elles
retournent une nouvelle chaîne.
Exemple :
word = word.strip()t = t.sort() # FAUX: t refere None !
il faut donc lire avec attention la documentation des fonctions,
méthodes et opérateurs; et écrire votre propre documentation de
manière claire. Le mode interactif est très utile pour tester
rapidement l’utilisation d’une fonction ou d’une méthode.
Programmation (Chap. 6) Listes 65 / 70
Erreurs fréquentes et debug
Erreurs fréquentes et debug
documentation des méthodes et opérateurs communs aux
séquences (comme les listes ou les chaînes) :
http://docs.python.org/lib/typesseq.html
documentation des méthodes et opérateurs applicables aux
séquences mutables :
http://docs.python.org/lib/typesseq-mutable.html
faire une copie quand c’est nécessaire : une méthode comme
sort modifie la liste sur laquelle elle est invoquée. Exemple de
copie avant modification :
orig = t[:]t.sort()
Programmation (Chap. 6) Listes 66 / 70
Erreurs fréquentes et debug
Erreurs fréquentes et debug
Choisissez une manière de faire, apprenez-la correctement, et
tenez vous-y.
Une partie des problèmes avec les listes : ∃ de nombreuses
manières de faire la même chose :
� pour supprimer un élément d’une liste : pop, remove, del ou
même [x:y].
� pour ajouter un élément d’une liste : append ou opérateur +
Programmation (Chap. 6) Listes 67 / 70
Erreurs fréquentes et debug
Erreurs fréquentes et debug
Ces instructions sont correctes pour ajouter un élément :
t.append(x)t = t + [x]
Ces instructions sont erronées :
t.append([x])t = t.append(x)t + [x]t = t + x
Exercice : essayez ces exemples interactivement pour comprendre ce
qu’ils font (seule la dernière instruction provoque une exception).
Programmation (Chap. 6) Listes 68 / 70
Glossaire
Glossaire
Programmation (Chap. 6) Listes 69 / 70
Glossaire
Glossaire
élément : une des valeurs dans une liste (ou une autre séquence).
indice : valeur entière qui indique la position d’un élément dans une liste (ou une
séquence).
liste : séquence de valeurs qui peuvent être modifiées et mises entre crochets droits.
liste imbriquée : liste qui est un élément d’une autre liste.
accumulateur : variable utilisée dans une boucle pour additionner ou accumuler un
résultat.
filtre : opération qui consiste à traverser une liste (ou une séquence) et qui sélectionne les
éléments satisfaisant un certain critère.
mapping : opération qui consiste à traverser une liste (ou une séquence) pour appliquer
une action sur chaque élément.
réduction : opération qui consiste à traverser une liste (ou une séquence) et qui accumule
les éléments en un seul résultat.
object : quelque chose qu’une variable peut référer. Un objet possède un type et une
valeur. On peut lui appliquer des méthodes définies en fonction de son type.
référence : l’association entre une variable et sa valeur (c-à-d la valeur de son objet).
alias : assigner une variable depuis une autre, pour qu’elles réfèrent au même objet.
équivalent : ayant la même valeur.
identique : être le même objet (ce qui implique l’équivalence).
Programmation (Chap. 6) Listes 70 / 70
Chapitre 7. Dictionnaires
Contenu du chapitre
1 Dictionnaires
2 Dictionnaires comme ensembles de compteurs
3 Méthodes des dictionnaires
4 Quelques algorithmes sur des dictionnaires
5 Glossaire
Programmation (Chap. 7) Dictionnaires 1 / 32
Dictionnaires
Dictionnaires
Programmation (Chap. 7) Dictionnaires 2 / 32
Dictionnaires
Dictionnaires
Un dictionnaire est une séquence mutable, comme une liste, mais estplus générale :
dans une liste, les indices doivent être des entiers;
dans un dictionnaire, ils peuvent être de (presque) n’importe queltype.
Un dictionnaire peut être vu comme une correspondance entre unensemble d’indices (les clefs) et de valeurs.
à chaque clef correspond une valeur;
l’association entre une clef et une valeur est une paire clef-valeurou un élément de la séquence.
Programmation (Chap. 7) Dictionnaires 3 / 32
Dictionnaires
DictionnairesSyntaxe.
lors de sa création, on utilise des accolades {.}. Pour rappel, les listesutilisent des crochets [.] et les tuples des parenthèses (.).
pour associer des clefs aux valeurs, on écrit une séquenceclef :valeur séparés par des virgules.
on peut également utiliser l’opérateur d’accès pour modifier undictionnaire d[clef] = valeur.
Exemple.Construisons un dictionnaire anglais-français : les clefs et les valeurs serontdes chaînes de caractères.
>>> eng2fr = {}>>> type(eng2fr)<type ’dict’>>>> eng2fr = {’one’: ’un’, ’two’ : ’deux’, ’three’ : ’trois’}>>> eng2fr[’four’] = ’quatre’>>> print eng2fr{’four’: ’quatre’, ’three’: ’trois’, ’two’: ’deux’, ’one’: ’un’}
Programmation (Chap. 7) Dictionnaires 4 / 32
Dictionnaires
Dictionnaires
L’ordre des valeurs peut paraître surprenant.>>> print eng2fr{’four’: ’quatre’, ’three’: ’trois’, ’two’: ’deux’, ’one’: ’un’}
En réalité, cet ordre peut même dépendre de la machine sur laquellevous exécutez ces instructions. En général, l’ordre des éléments d’undictionnaire est imprévisible.
Ce n’est pas un problème : on n’accède pas aux éléments avec desindices entiers, on utilise les clefs.>>> print eng2fr[’two’]deux>>> print eng2fr[’five’]KeyError: ’five’
Programmation (Chap. 7) Dictionnaires 5 / 32
Dictionnaires
DictionnairesConcernant les types :
les valeurs peuvent être de n’importe quel type (comme pour leslistes)les clefs ne peuvent pas être d’un type mutable. Ex. de typesacceptés : chaînes de caractères, entiers, ou tuples (cf. chap.suivant).Plus précisément, le type d’une clef doit être “hachable”, c-à-dque l’on peut le convertir en un nombre entier d’une manièredéterministe. Les objets mutables ne possèdent pas cettepropriété.
>>> hash(6)6>>> hash(’hello’)-1267296259>>> hash( (1, 2) )1299869600>>> hash([1, 2, 3])TypeError: unhashable type: ’list’
Programmation (Chap. 7) Dictionnaires 6 / 32
Dictionnaires
Dictionnaires
Exemple :>>> d = {’entier’ : 1, ’liste’ : [1, 2, 3], 3 : ’trois’, ’dico’ : eng2fr}>>> d{’liste’: [1, 2, 3], ’dico’: {’four’: ’quatre’, ’three’: ’trois’, ’two’: ’deux’, ’one’: ’un’},3: ’trois’, ’entier’: 1}>>> d[’entier’]1>>> d[’liste’][1, 2, 3]>>> d[3]’trois’>>> d[[1, 2]] = ’liste comme clef?’
TypeError: unhashable type: ’list’>>> d[’dico’][’two’]’deux’
Programmation (Chap. 7) Dictionnaires 7 / 32
Dictionnaires
Un dictionnaire est une séquence
Un dictionnaire est une séquence : on peut lui appliquer certainesopérations déjà vues avec les chaînes et les listes.
fonction len
>>> len(eng2fr)4
opérateur in : il s’applique pour rechercher une clef, pas unevaleur (intuition : nous avons construit un dictionnaireanglais-français, pas l’inverse).>>> ’one’ in eng2frTrue>>> ’deux’ in eng2frFalse
par contre on ne pourra pas utiliser le “slice” puisqu’on netravaille pas avec des indices entiers.
Programmation (Chap. 7) Dictionnaires 8 / 32
Dictionnaires
Un dictionnaire est une séquence
Pour déterminer si une valeur est présente dans le dictionnaire, onpeut utiliser la méthode values qui retourne toutes les valeurs sous laforme d’une liste.>>> vals = eng2fr.values()>>> ’deux’ in valsTrue>>> print vals[’quatre’, ’trois’, ’deux’, ’un’]
Programmation (Chap. 7) Dictionnaires 9 / 32
Dictionnaires
Un dictionnaire est une séquence
On peut appliquer une boucle for sur un dictionnaire : elle traverse lesclefs du dictionnaire.>>> for k in eng2fr:
print k, eng2fr[k]
four quatrethree troistwo deuxone un
Programmation (Chap. 7) Dictionnaires 10 / 32
Dictionnaires comme ensembles de compteurs
Dictionnaires commeensembles de compteurs
Programmation (Chap. 7) Dictionnaires 11 / 32
Dictionnaires comme ensembles de compteurs
Dictionnaires comme ensembles de compteursProblème (Histogramme de la fréquence des lettres)Comment comptez la fréquence de chaque lettre dans une chaîne decaractères ?
Plusieurs solutions :
créer 26 variables, une pour chaque lettre. Traverser la chaîne et, pourchaque caractère, incrémenter le compteur correspondant, par ex. enutilisant une instruction conditionnelle chaînée (26 cas !).
créer une liste de 26 éléments puis convertir chaque caractère en unnombre (en utilisant la fonction ord par exemple). Utiliser ce nombrecomme indice de la liste pour incrémenter le compteur approprié.
créer un dictionnaire avec les caractères comme clefs et les compteurscomme valeurs. La première fois que l’on rencontre un caractère, onl’ajoute dans le dictionnaire avec une valeur 1. Lors d’une prochaineoccurrence de la lettre, on incrémente la valeur.
Avantage du dictionnaire : code simplifié et on utilise uniquement lescompteurs utiles.Programmation (Chap. 7) Dictionnaires 12 / 32
Dictionnaires comme ensembles de compteurs
Dictionnaires comme ensembles de compteurs
Solution utilisant un dictionnaire :def histogram(s):
d = {}for c in s:
if c not in d:d[c] = 1
else:d[c] += 1
return d
>>> h = histogram(’brontosaurus’)>>> print h{’a’: 1, ’b’: 1, ’o’: 2, ’n’: 1, ’s’: 2, ’r’: 2, ’u’: 2, ’t’: 1}
Programmation (Chap. 7) Dictionnaires 13 / 32
Méthodes des dictionnaires
Méthodes des dictionnaires
Programmation (Chap. 7) Dictionnaires 14 / 32
Méthodes des dictionnaires
Méthodes des dictionnaires
Les objets de type dictionnaire possèdent une série de méthodes.>>> dir(eng2fr)[’__class__’, ’__cmp__’, ’__contains__’, ’__delattr__’, ’__delitem__’,...’clear’, ’copy’, ’fromkeys’, ’get’, ’has_key’, ’items’, ’iteritems’,’iterkeys’, ’itervalues’, ’keys’, ’pop’, ’popitem’, ’setdefault’,’update’, ’values’]
Nous allons en voir quelques unes.
Programmation (Chap. 7) Dictionnaires 15 / 32
Méthodes des dictionnaires
Méthodes des dictionnaires
La méthode clear permet de supprimer tous les éléments dudictionnaire.>>> help(dict.clear)Help on method_descriptor:
clear(...)D.clear() -> None. Remove all items from D.
>>> d = {’yes’ : ’oui’, ’no’ : ’non’}>>> print d{’yes’: ’oui’, ’no’: ’non’}>>> d.clear()>>> d{}
Programmation (Chap. 7) Dictionnaires 16 / 32
Méthodes des dictionnaires
Méthodes des dictionnaires
La méthode copy permet de faire une copie d’un dictionnaire (qui n’estpas un alias).>>> help(dict.copy)Help on method_descriptor:
copy(...)D.copy() -> a shallow copy of D
>>> d = {’yes’ : ’oui’, ’no’ : ’non’}>>> e = d.copy()>>> e{’yes’: ’oui’, ’no’: ’non’}>>> e[’yes’] = ’Oui’>>> e{’yes’: ’Oui’, ’no’: ’non’}>>> d{’yes’: ’oui’, ’no’: ’non’}
Programmation (Chap. 7) Dictionnaires 17 / 32
Méthodes des dictionnaires
Méthodes des dictionnairesLa méthode get prend une clef et une valeur par défaut en paramètre.Si la clef est présente dans le dictionnaire, get retourne la valeurcorrespondante. Sinon, elle retourne la valeur par défaut.>>> help(dict.get)Help on method_descriptor:
get(...)D.get(k[,d]) -> D[k] if k in D, else d. d defaults to None.
>>> eng2fr.get(’one’,’Mot inconnu’)’un’>>> eng2fr.get(’five’,’Mot inconnu’)’Mot inconnu’
Ceci permet de réécrire histogram sans instruction conditionnelleexplicite.def histogram(s):
d = {}for c in s:
d[c] = d.get(c,0) + 1return d
Programmation (Chap. 7) Dictionnaires 18 / 32
Méthodes des dictionnaires
Méthodes des dictionnaires
La méthode setdefault prend une clef et une valeur par défaut enparamètre. Si la clef existe : retourne la valeur, si la clef n’existe pas :ajoute la paire (clef, valeur par defaut) et retourne la valeur par défaut.>>> help(d.setdefault)Help on built-in function setdefault:
setdefault(...)D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D
>>> eng2fr.setdefault(’two’,’dos’)’deux’>>> eng2fr.setdefault(’ten’, ’dix’)’dix’
Programmation (Chap. 7) Dictionnaires 19 / 32
Méthodes des dictionnaires
Méthodes des dictionnaires
La méthode has_key permet de savoir si une clef est présente dans ledictionnaire.>>> help(dict.has_key)Help on method_descriptor:
has_key(...)D.has_key(k) -> True if D has a key k, else False
>>> eng2fr.has_key(’one’)True>>> eng2fr.has_key(’five’)False
Programmation (Chap. 7) Dictionnaires 20 / 32
Méthodes des dictionnaires
Méthodes des dictionnaires
Les méthodes keys, values et items permettent d’obtenirrespectivement les clefs, les valeurs et des 2-uples “clef-valeur” sousforme de listes.>>> help(dict.keys)Help on method_descriptor:
keys(...)D.keys() -> list of D’s keys
>>> help(dict.values)Help on method_descriptor:
values(...)D.values() -> list of D’s values
>>> eng2fr.keys()[’four’, ’three’, ’two’, ’one’]>>> eng2fr.values()[’quatre’, ’trois’, ’deux’, ’un’]
Programmation (Chap. 7) Dictionnaires 21 / 32
Méthodes des dictionnaires
Méthodes des dictionnaires
>>> help(dict.items)Help on method_descriptor:
items(...)D.items() -> list of D’s (key, value) pairs, as 2-tuples
>>> eng2fr.items()[(’four’, ’quatre’), (’three’, ’trois’), (’two’, ’deux’), (’one’, ’un’)]
Ces trois méthodes sont suffisantes pour itérer sur l’ensemble d’undictionnaire, soit par les clefs, les valeurs ou les deux. Cependant,elles nécessitent la création d’une nouvelle liste.
Programmation (Chap. 7) Dictionnaires 22 / 32
Méthodes des dictionnaires
Méthodes des dictionnairesPour éviter de créer une liste : utiliser un itérateur. Un itérateurpeut-être vu comme une “flêche” vers un élément d’une séquence, quipermet de la traverser directement.
On utilise les méthodes iterkeys, itervalues et iteritems quiretournent un itérateur (sur les clefs, les valeurs ou les 2, respect.).Exemple :>>> for i in eng2fr.iterkeys():
print i,
four three two one>>> for i in eng2fr.itervalues():
print i,
quatre trois deux un>>> for i in eng2fr.iteritems():
print i,
(’four’, ’quatre’) (’three’, ’trois’) (’two’, ’deux’) (’one’, ’un’)
Programmation (Chap. 7) Dictionnaires 23 / 32
Méthodes des dictionnaires
Méthodes des dictionnaires
La méthode pop supprime une paire clef-valeur à partir d’une clef etretourne la valeur supprimée.>>> help(dict.pop)Help on method_descriptor:
pop(...)D.pop(k[,d]) -> v, remove specified key and return the corresponding value.If key is not found, d is returned if given, otherwise KeyError is raised
>>> print eng2fr.pop(’five’, None)None>>> print eng2fr.pop(’one’, None)un>>> eng2fr{’four’: ’quatre’, ’three’: ’trois’, ’two’: ’deux’}
Programmation (Chap. 7) Dictionnaires 24 / 32
Méthodes des dictionnaires
Méthodes des dictionnaires
La méthode popitem supprime une paire clef-valeur (indéterminée) etretourne la paire supprimée sous la forme d’un 2-uple.>>> help(dict.popitem)Help on method_descriptor:
popitem(...)D.popitem() -> (k, v), remove and return some (key, value) pair as a2-tuple; but raise KeyError if D is empty.
>>> eng2fr.popitem()(’four’, ’quatre’)>>> eng2fr{’three’: ’trois’, ’two’: ’deux’}
Programmation (Chap. 7) Dictionnaires 25 / 32
Quelques algorithmes sur des dictionnaires
Quelques algorithmes sur desdictionnaires
Programmation (Chap. 7) Dictionnaires 26 / 32
Quelques algorithmes sur des dictionnaires
Quelques algorithmes sur des dictionnaires
Trouver une valeur à partir d’une clef est trivial, mais le contraire ?
ProblèmeComment trouver la (les) clef(s) d’une valeur donnée ?
Idée : on va traverser linéairement le dictionnaire et retourner une listeavec les clefs correspondant à une valeur donnée (les clefs sontuniques, mais il peut y avoir une même valeur pour différentes clefs).
Programmation (Chap. 7) Dictionnaires 27 / 32
Quelques algorithmes sur des dictionnaires
Quelques algorithmes sur des dictionnaires
Trouver une valeur à partir d’une clef est trivial, mais le contraire ?
ProblèmeComment trouver la (les) clef(s) d’une valeur donnée ?
Idée : on va traverser linéairement le dictionnaire et retourner une listeavec les clefs correspondant à une valeur donnée (les clefs sontuniques, mais il peut y avoir une même valeur pour différentes clefs).
Programmation (Chap. 7) Dictionnaires 27 / 32
Quelques algorithmes sur des dictionnaires
Quelques algorithmes sur des dictionnaires
def get_keys(d, value):t = []for k in d:
if d[k] == value:t.append(k)
return t
>>> d = histogram(’evenement’)>>> d{’m’: 1, ’n’: 2, ’e’: 4, ’t’: 1, ’v’: 1}>>> get_keys(d,4)[’e’]>>> get_keys(d,1)[’m’, ’t’, ’v’]
Programmation (Chap. 7) Dictionnaires 28 / 32
Quelques algorithmes sur des dictionnaires
Quelques algorithmes sur des dictionnaires
La fonction histogram est pratique mais on pourrait avoir besoind’obtenir ces informations de manière inversée.
Exemple : Au lieu de {’m’: 1, ’n’: 2, ’e’: 4, ’t’: 1, ’v’: 1}, onaimerait avoir {1 : [’m’, ’t’, ’v’], 2 : [’n’], 4: [’e’]}.
ProblèmeComment inverser les clefs et les valeurs d’un dictionnaire ?
Idée : on va créer un nouveau dictionnaire qui contiendra des listes :pour chaque valeur du dictionnaire de départ, on ajoute une pairedans le nouveau dictionnaire si celle-ci n’est pas présente, sinon, onmet à jour la paire.
Programmation (Chap. 7) Dictionnaires 29 / 32
Quelques algorithmes sur des dictionnaires
Quelques algorithmes sur des dictionnaires
La fonction histogram est pratique mais on pourrait avoir besoind’obtenir ces informations de manière inversée.
Exemple : Au lieu de {’m’: 1, ’n’: 2, ’e’: 4, ’t’: 1, ’v’: 1}, onaimerait avoir {1 : [’m’, ’t’, ’v’], 2 : [’n’], 4: [’e’]}.
ProblèmeComment inverser les clefs et les valeurs d’un dictionnaire ?
Idée : on va créer un nouveau dictionnaire qui contiendra des listes :pour chaque valeur du dictionnaire de départ, on ajoute une pairedans le nouveau dictionnaire si celle-ci n’est pas présente, sinon, onmet à jour la paire.
Programmation (Chap. 7) Dictionnaires 29 / 32
Quelques algorithmes sur des dictionnaires
Quelques algorithmes sur des dictionnaires
def invert_dict(d):inv = {}for k in d:
val = d[k]if val not in inv:
inv[val] = [k]else:
inv[val].append(k)return inv
>>> d = histogram(’evenement’)>>> inv_d = invert_dict(d)>>> inv_d{1: [’m’, ’t’, ’v’], 2: [’n’], 4: [’e’]}
Programmation (Chap. 7) Dictionnaires 30 / 32
Glossaire
Glossaire
Programmation (Chap. 7) Dictionnaires 31 / 32
Glossaire
Glossaire
dictionnaire : séquence mutable de paires “clef-valeur”.
hachable : propriété d’un type immuable lui permettant d’être converti de manièredéterministe en un nombre entier.
Programmation (Chap. 7) Dictionnaires 32 / 32
Chapitre 8. Tuples
Contenu du chapitre
1 Un tuple est une séquence immuable
2 Fonctions avec un nombre variable d’arguments
3 Listes et tuples
4 Dictionnaires et tuples
5 Comparaison de tuples
6 Glossaire
Programmation (Chap. 8) Tuples 1 / 29
Un tuple est une séquence immuable
Un tuple est une séquence
immuable
Programmation (Chap. 8) Tuples 2 / 29
Un tuple est une séquence immuable
Un tuple est une séquence immuable
Un tuple est une séquence de valeurs. On définit un tuple en
spécifiant les valeurs, séparées par des virgules, entre parenthèses1
>>> t = (’a’, ’b’, ’c’)
>>> type(t)
<type ’tuple’>
>>> print t
(’a’, ’b’, ’c’)
>>> t = ’a’, ’b’
>>> type(t)
<type ’tuple’>
1L’utilisation des parenthèses est facultative, mais est souvent utilisée pour plus de
clarté.
Programmation (Chap. 8) Tuples 3 / 29
Un tuple est une séquence immuable
Un tuple est une séquence immuable
les valeurs peuvent être de n’importe quel type
les valeurs sont indicées par des entiers
les tuples sont immuables
>>> t = (’a’, 1, [1, 2])
>>> t[0]
’a’
>>> t[-1]
[1, 2]
>>> t[0] = ’b’
TypeError: ’tuple’ object does not support item assignment
Programmation (Chap. 8) Tuples 4 / 29
Un tuple est une séquence immuable
Un tuple est une séquence immuable
Pour distinguer syntaxiquement une unique valeur et un tuple
contenant une seule valeur, on doit utiliser une virgule après la seule
valeur du tuple.
>>> t = (’a’,)
>>> print type(t), t
<type ’tuple’> (’a’,)
>>> t = ’a’,
>>> print type(t), t
<type ’tuple’> (’a’,)
>>> t = (’a’)
>>> print type(t), t
<type ’str’> a
Ce sont donc les virgules (plutôt que les parenthèses) qui définissent
syntaxiquement les tuples. Celles-ci sont utilisées par convention (car proche
de la notation mathématique des n-uples).
Programmation (Chap. 8) Tuples 5 / 29
Un tuple est une séquence immuable
Un tuple est une séquence immuable
Pour créer un tuple vide, on peut utiliser des parenthèses ne
contenant rien ou la fonction tuple sans arguments.
Si l’argument de la fonction tuple est une séquence, elle retourne un
tuple avec les éléments de celle-ci2.
>>> x = ()
>>> y = tuple()
>>> print x, y, type(x), type(y)
() () <type ’tuple’> <type ’tuple’>
>>> s = ’abc’
>>> t = range(3)
>>> d = {’a’:1, ’b’:2, ’c’:3}
>>> tuple(s)
(’a’, ’b’, ’c’)
>>> tuple(t)
(0, 1, 2)
>>> tuple(d)
(’a’, ’c’, ’b’)
2Dans le cas des dictionnaires, retourne un tuple des clefs.
Programmation (Chap. 8) Tuples 6 / 29
Un tuple est une séquence immuable
Comparaisons des différents types de séquences (vues)
type mutable ? indices type des valeurs syntaxe
str non entiers caractères ’abc’
list oui entiers tous types [’a’, ’b’, ’c’]
dict oui type hachable tous types {’a’:4, ’b’:2}
tuple non entiers tous types (a, b, c)
Les opérateurs applicables à une séquence immuable avec des indices
entiers sont également disponibles sur les tuples, par ex. : accès d’un
élément via [.], tranches (slices), concaténation (+), opérateur *, fonction
len, boucles for, opérateur in.
>>> t = (’a’, ’B’, ’C’)
>>> t[0] = ’A’
TypeError: ’tuple’ object does not support item assignment
>>> t = (’A’,) + t[1:]
>>> t
(’A’, ’B’, ’C’)
Programmation (Chap. 8) Tuples 7 / 29
Un tuple est une séquence immuable
Assignation de tuples
Un tuple de variables peut se placer à gauche d’une assignation. Il
faut que le membre droit soit un tuple d’expressions ayant le même
nombre d’éléments.
>>> a = 17
>>> b = 21
>>> (a, b) = (b, a)
>>> print a, b
21 17
>>> a, b = 1 * 2, 3 + 4
>>> print a, b
2 7
>>> a, b = 1, 2, 3
ValueError: too many values to unpack
Programmation (Chap. 8) Tuples 8 / 29
Un tuple est une séquence immuable
Assignation de tuples
De manière plus générale, le membre droit de l’assignation peut être
n’importe quel type de séquence, du moment que le nombre
d’éléments correspond.
>>> a, b, c = ’xyz’
>>> print a
x
>>> uname, domain = ’[email protected]’.split(’@’)
>>> print uname
jack
>>> print domain
umons.ac.be
>>> key1, key2 = {’a’:12, ’b’:21}
>>> print key2
b
Programmation (Chap. 8) Tuples 9 / 29
Un tuple est une séquence immuable
Assignation de tuples
Strictement parlant, une fonction ne peut retourner qu’une seule
valeur. Utiliser un tuple comme valeur de retour permet de contourner
cette limitation.
def min_max(t):
min = max = t[0]
for k in t:
if k > max:
max = k
if k < min:
min = k
return min, max
>>> print min_max(range(3,8))
(3, 7)
Programmation (Chap. 8) Tuples 10 / 29
Fonctions avec un nombre variable d’arguments
Fonctions avec un nombre
variable d’arguments
Programmation (Chap. 8) Tuples 11 / 29
Fonctions avec un nombre variable d’arguments
Fonctions avec un nombre variable d’arguments
Une fonction peut prendre un nombre variable d’arguments. Par
exemple les fonctions min et max peuvent être appliquées sur des
séquences, mais également sur un nombre indéfini d’arguments.
>>> help(max)
Help on built-in function max in module __builtin__:
max(...)
max(iterable[, key=func]) -> value
max(a, b, c, ...[, key=func]) -> value
With a single iterable argument, return its largest item.
With two or more arguments, return the largest argument.
>>> max(3, 8, 5)
8
>>> a = 2
>>> b = 5
>>> c = 1
>>> d = 2
>>> max(a, b, c, d)
5
>>> min(a,b)
2
Programmation (Chap. 8) Tuples 12 / 29
Fonctions avec un nombre variable d’arguments
Fonctions avec un nombre variable d’arguments
Pour définir ses propres fonctions avec un nombre indéfini
d’arguments, on utilise un paramètre dont le nom commence par ∗.
Ce paramètre rassemble (gather) les arguments en un tuple, quelque
soit leur nombre.
def f(*args):
print type(args), ’of length’, len(args)
print args
>>> f(2, 4)
<type ’tuple’> of length 2
(2, 4)
>>> f(’hello’,2.0,[1, 2])
<type ’tuple’> of length 3
(’hello’, 2.0, [1, 2])
Remarque : il ne peut y avoir qu’un seul paramètre de ce type, et il
doit se trouver à la fin de la liste des paramètres.
Programmation (Chap. 8) Tuples 13 / 29
Fonctions avec un nombre variable d’arguments
Fonctions avec un nombre variable d’arguments
On peut combiner l’opérateur gather ∗ avec des paramètres requis et
optionnels (avec valeurs par défaut).
On commence par les paramètres requis, puis les optionnels et enfin
le paramètre de taille variable.
def g(required, optional=0, *args):
print ’required:’, required
print ’optional:’, optional
print ’others:’, args
Programmation (Chap. 8) Tuples 14 / 29
Fonctions avec un nombre variable d’arguments
Fonctions avec un nombre variable d’arguments
>>> g()
TypeError: g() takes at least 1 argument (0 given)
>>> g(1)
required: 1
optional: 0
others: ()
>>> g(1,2)
required: 1
optional: 2
others: ()
>>> g(1,2,3)
required: 1
optional: 2
others: (3,)
>>> g(1,2,3,4)
required: 1
optional: 2
others: (3, 4)
Programmation (Chap. 8) Tuples 15 / 29
Fonctions avec un nombre variable d’arguments
Opérateur ∗: gather and scatter
Gather : Utilisé devant le nom d’un paramètre dans l’en-tête d’une
fonction, l’opérateur ∗ rassemble les arguments en un tuple.
Scatter : L’opérateur ∗ appliqué sur un tuple lors de l’appel à une
fonction permet également de faire l’inverse : il sépare le tuple en une
séquence d’arguments.
>>> help(divmod)
Help on built-in function divmod in module __builtin__:
divmod(...)
divmod(x, y) -> (div, mod)
Return the tuple ((x-x%y)/y, x%y). Invariant: div*y + mod == x.
>>> t = (7,3)
>>> divmod(t)
TypeError: divmod expected 2 arguments, got 1
>>> divmod(*t)
(2, 1)
Programmation (Chap. 8) Tuples 16 / 29
Listes et tuples
Listes et tuples
Programmation (Chap. 8) Tuples 17 / 29
Listes et tuples
Listes et tuples
La fonction zip prend deux ou plusieurs séquences en paramètre et
les fusionne en une liste de tuples. Si le nombre d’éléments des
séquences n’est pas le même, la liste retournée a une taille
équivalente à la taille de la plus petite séquence.
>>> s = ’abc’
>>> t = range(3)
>>> zip(s, t)
[(’a’, 0), (’b’, 1), (’c’, 2)]
>>> zip(’Joe’,’Jack’)
[(’J’, ’J’), (’o’, ’a’), (’e’, ’c’)]
Programmation (Chap. 8) Tuples 18 / 29
Listes et tuples
Listes et tuples
L’assignation de tuples peut être utilisée dans une boucle for
>>> t = zip(’abc’,range(3))
>>> for letter, number in t:
print number, letter
0 a
1 b
2 c
Programmation (Chap. 8) Tuples 19 / 29
Listes et tuples
Listes et tuples
Combiner zip, une boucle for et l’assignation de tuples permet de
traverser en une fois plusieurs séquences.
Par exemple, la fonction suivante retourne vrai ssi une valeur est
identique dans les deux séquences pour au moins un indice.
def has_match(t1, t2):
for x, y in zip(t1,t2):
if x == y:
return True
return False
Remarque : le code ci-dessus est beaucoup plus intuitif que celui-ci :
for i in range(min(len(t1), len(t2))):
if t1[i] == t2[i]:
Programmation (Chap. 8) Tuples 20 / 29
Listes et tuples
Listes et tuples
Si l’on doit traverser une séquence et travailler à la fois sur les indices
et les valeurs, on peut utiliser la fonction enumerate qui, à chaque
itération, assigne un tuple (indice, valeur), pour chaque élément de la
séquence.
>>> for index, value in enumerate(’abc’):
print index, value
0 a
1 b
2 c
Programmation (Chap. 8) Tuples 21 / 29
Dictionnaires et tuples
Dictionnaires et tuples
Programmation (Chap. 8) Tuples 22 / 29
Dictionnaires et tuples
Dictionnaires et tuples
Les dictionnaires ont une méthode items qui retourne une liste de
tuples : chaque tuple est une paire clef-valeur (dont l’ordre est
indéterminé).
Inversément, on peut utiliser une liste de tuples pour initialiser un
dictionnaire avec la fonction dict. Combiner dict et zip permet de
créer de manière concise un dictionnaire.
>>> d = {’a’:0, ’b’:1, ’c’:2}
>>> t = d.items()
>>> print t
[(’a’, 0), (’c’, 2), (’b’, 1)]
>>> t = [(’d’, 3), (’e’, 4), (’f’, 5)]
>>> d = dict(t)
>>> d
{’e’: 4, ’d’: 3, ’f’: 5}
>>> d = dict(zip(’abc’,range(3)))
>>> print d
{’a’: 0, ’c’: 2, ’b’: 1}
Programmation (Chap. 8) Tuples 23 / 29
Dictionnaires et tuples
Dictionnaires et tuples
La méthode update des dictionnaires prend une liste de tuples en
argument et les ajoute au dictionnaire existant.
En combinant items, l’assignation de tuples et une boucle for, on
peut facilement traverser les clefs et les valeurs d’un dictionnaire.
>>> d[’a’] = ’0’
>>> d.update(zip(’bcd’,range(1,4)))
>>> for key, val in d.items():
print key, val
a 0
c 2
b 1
d 3
Programmation (Chap. 8) Tuples 24 / 29
Dictionnaires et tuples
Dictionnaires et tuples
Il est fréquent d’utiliser des tuples comme clefs d’un dictionnaire
(principalement car on ne peut pas utiliser des listes).
Par exemple, on peut utiliser un dictionnaire pour stocker un répertoire
téléphonique dont les clefs sont un tuple (nom, prénom).
>>> tel = {}
>>> tel[’Baroud’,’Bill’] = ’065-37-07-56’
>>> tel[’II’,’Albert’] = ’02-256-89-14’
>>> tel[’Poelvoorde’,’Benoit’] = ’081-23-89-65’
>>> for last, first in tel:
print first, last, tel[last, first]
Benoit Poelvoorde 081-23-89-65
Bill Baroud 065-37-07-56
Albert II 02-256-89-14
Programmation (Chap. 8) Tuples 25 / 29
Comparaison de tuples
Comparaison de tuples
Programmation (Chap. 8) Tuples 26 / 29
Comparaison de tuples
Comparaison de tuples
Les opérateurs de comparaison fonctionnent avec les tuples (et les
autres séquences). Python commence par comparer le premier
élément de chaque séquence. S’ils sont égaux, il compare l’élément
suivant et ainsi de suite jusqu’à trouver le premier élément qui diffère.
La comparaison s’effectue sur ce premier élément qui diffère, les
autres éléments sont ignorés, quelles que soient leurs valeurs.
On parle d’ordre lexicographique (car c’est le même comportement
pour des chaînes de caractères).
>>> (0, 1, 2) < (0, 3, 1)
True
>>> (0, 1, 20000000) < (0, 3, 1)
True
>>> (0, 1, 2) < (0, 1, 1)
False
Programmation (Chap. 8) Tuples 27 / 29
Glossaire
Glossaire
Programmation (Chap. 8) Tuples 28 / 29
Glossaire
Glossaire
tuple : séquence immuable d’éléments.
gather : opération qui consiste à assembler un argument de taille variable en un tuple
(opérateur ∗ dans l’en-tête d’une définition de fonction).
scatter : opération qui consiste à traiter une séquence comme une liste d’arguments
(opérateur ∗ lors de l’appel d’une fonction).
Programmation (Chap. 8) Tuples 29 / 29
Chapitre 9. Récursivité
Contenu du chapitre
1 Concept de récursivité
2 Glossaire
Programmation (Chap. 9) Récursivité 1 / 33
Concept de récursivité
Concept de récursivité
Programmation (Chap. 9) Récursivité 3 / 33
Concept de récursivité
Preuve par récurrence
La récursivité est une notion classique en mathématiques quand il
s’agit de prouver une assertion (preuve par récurrence) : cf. cours
Mathématiques élémentaires
Rappel du principe
A prouver : assertion S(n) = f (n) ∀n ≥ n0
Base : preuve de l’assertion pour certaines petites valeurs de n (n0
par exemple)
Etape de récurrence : on suppose que c’est vrai pour n ≤ k (avec
k ≥ n0), prouver que c’est également vrai pour n = k +1
Remarque : on peut aussi supposer que c’est vrai pour n ≤ k−1 et prouver
que c’est vrai pour n = k . C’est ce qu’on fera ici : souvent plus intuitif d’un
point de vue informatique.
Programmation (Chap. 9) Récursivité 4 / 33
Concept de récursivité
Preuve par récurrence
Exemple
Soit S(n) = ∑ni=1 i . Prouver que
S(n) =n(n +1)
2∀n ≥ 1 (1)
Base : si n = 1, alors S(1) = 1 par définition et (1) devient
1 =1×2
2,
ce qui est exact.
Programmation (Chap. 9) Récursivité 5 / 33
Concept de récursivité
Preuve par récurrence
Etape de récurrence : on suppose que (1) est vraie pour
1≤ n ≤ k−1, prouvons que c’est également vrai pour n = k . Par
hypothèse de récurrence,
S(k−1) =(k−1)k
2(2)
Comment exprimer S(k) en fonction de S(k−1) ? Par définition,
S(k) =k
∑i=1
i =k−1
∑i=1
i + k = S(k−1)+ k .
Par (2), il s’ensuit
S(k) =(k−1)k
2+ k .
Or,
(k−1)k2
+ k =k2− k
2+
2k2
=k2 + k
2=
k(k +1)
2.
Programmation (Chap. 9) Récursivité 6 / 33
Concept de récursivité
Preuve par récurrence
On a donc bien
S(n) =n(n +1)
2∀n ≥ 1.
Qu’avons nous du déterminer pour utiliser une preuve par
récurrence ?
(base) résoudre un cas simple
(étape de récurrence)
� supposer le résultat vrai pour n ≤ k−1
� exprimer S(k) en fonction de S(k−1) (ou en fonction d’autres
valeurs plus petites que k−1)
� retrouver le résultat en combinant les deux points ci-dessus
Programmation (Chap. 9) Récursivité 7 / 33
Concept de récursivité
Récursivité
En informatique, on peut également utiliser la récursivité
Problème
Comment calculer la somme des n premiers nombres entiers ?
Solution 1 : utiliser la formule prouvée précédemment (formule d’Euler)
>>> def sum_1_to_n(n):return n * (n + 1) / 2
>>> print sum_1_to_n(10)
55
Programmation (Chap. 9) Récursivité 8 / 33
Concept de récursivité
Récursivité
Solution 2 : utiliser la récursivité, c’est à dire la possibilité pour une
fonction de s’appeler elle même.
(base) Si n = 1, alors la somme S(n) vaut 1
(étape de récurrence) Si n > 1, S(n) = S(n−1)+n
Ces deux éléments suffisent pour résoudre le problème de manière
récursive
>>> def sum_rec(n):if n == 1:
return 1else:
return sum_rec(n - 1) + n
>>> print sum_rec(10)
55
Programmation (Chap. 9) Récursivité 9 / 33
Concept de récursivité
Récursivité
Intuition : le programmeur paresseux
Supposons que Bill soit paresseux et qu’il doive résoudre un problème
complexe. Il préfère laisser à Bob le soin de faire le gros du travail. Si
Bill est capable de
résoudre le problème pour les cas les plus simples;
récupérer le travail de Bob et réaliser un travail de taille un peu
plus grande à partir de celui-ci;
alors le problème peut être résolu par récursivité, de manière simple.
Exemple : Bill doit calculer S(n) = ∑ni=1 i . Bill peut demander à Bob de
calculer S(k) pour k = 2,3, . . .n−1. Il lui reste simplement à
calculer S(1),
déterminer comment calculer S(n) en utilisant S(n−1) (ou
d’autres valeurs calculées par Bob).
Programmation (Chap. 9) Récursivité 10 / 33
Concept de récursivité
Récursivité
Problème
Ecrire une fonction qui calcule la factorielle d’un nombre entier.
Une solution récursive :
(base) 0! = 1
(étape de récurrence) n! = n× (n−1)!
Programmation (Chap. 9) Récursivité 11 / 33
Concept de récursivité
Récursivité
Problème
Ecrire une fonction qui calcule la factorielle d’un nombre entier.
Une solution récursive :
(base) 0! = 1
(étape de récurrence) n! = n× (n−1)!
Programmation (Chap. 9) Récursivité 11 / 33
Concept de récursivité
Récursivité
En Python :
>>> def factorial(n):if n == 0:
return 1else:
recurse = factorial(n - 1)result = n * recursereturn result
>>> print factorial(4)
24
Plus simplement :
>>> def factorial(n):if n == 0:
return 1else:
return n * factorial(n - 1)
Programmation (Chap. 9) Récursivité 12 / 33
Concept de récursivité
Récursivité
Problème
Ecrire une fonction qui calcule an, si n ≥ 1.
Une solution récursive :
(base) a1 = a
(étape de récurrence) an = an−1×a
Programmation (Chap. 9) Récursivité 13 / 33
Concept de récursivité
Récursivité
Problème
Ecrire une fonction qui calcule an, si n ≥ 1.
Une solution récursive :
(base) a1 = a
(étape de récurrence) an = an−1×a
Programmation (Chap. 9) Récursivité 13 / 33
Concept de récursivité
Récursivité
En Python :
>>> def expo(a, n):if n == 1:
return aelse:
return expo(a,n - 1) * a
>>> print expo(3,3), (3**3)
27 27
Programmation (Chap. 9) Récursivité 14 / 33
Concept de récursivité
Rappels concernant les fonctions
Au chapitre 3, nous avons vu que :
une fonction peut être utilisée dans une expression comme une valeur
(= sa valeur de retour)
une fonction peut en appeler une autre
La récursivité n’est rien d’autre que l’application de ses deux faits sur la
fonction elle-même !
def expo(a, n):if n == 1:
return aelse:
return expo(a,n - 1) * a
Pour comprendre et valider une fonction récursive, il s’agit
d’accepter qu’un appel récursif retourne la bonne valeur
de s’assurer que le(s) cas de base(s) soi(en)t bien géré(s)
de s’assurer que chaque appel récursif réduise la taille du problème
jusqu’à atteindre un cas de base
Programmation (Chap. 9) Récursivité 15 / 33
Concept de récursivité
Récursivité
Les exemples précédents montrent que l’on peut résoudre très
simplement certains problèmes si on arrive à :
résoudre le problème pour les cas les plus simples;
récupérer une solution pour une certaine taille du problème et
construire une solution de plus grande taille à partir de celui-ci.
Dans beaucoup de cas, ces deux tâches sont plus faciles à résoudre
qu’une solution “générale” ou “complète” au problème !
Mais. . . cela peut paraître “magique” : on peut avoir du mal à
“accepter” que cela fonctionne aussi simplement.
On peut analyser le flot d’exécution pour s’en convaincre.
Programmation (Chap. 9) Récursivité 16 / 33
Concept de récursivité
Récursivité et flot d’exécution
Analysons le flot d’exécution d’un appel à factorial(3)
>>> def factorial(n):if n == 0:
return 1else:
recurse = factorial(n - 1)result = n * recursereturn result
>>> print factorial(3)
6
Programmation (Chap. 9) Récursivité 17 / 33
Concept de récursivité
Analyse du flot d’exécution lors de l’appel factorial(3)• appel à factorial(n→ 3) : comme n �= 0, appel à factorial(n→ 2)
• détail de l’appel à factorial(n→ 2) :
appel à factorial(n→ 2) : comme n �= 0, appel à factorial(n→ 1)
détail de l’appel à factorial(n→ 1) :
� appel à factorial(n→ 1) : comme n �= 0, appel à
factorial(n→ 0)
� détail de l’appel à factorial(n→ 0) :� appel à factorial(n→ 0) : comme n = 0, retourne 1
� valeur de retour de factorial(n→ 0) est 1 (recurse→ 1), puis
multipliée par 1 (result→ 1) : appel à factorial(n→ 1)
terminé, retourne 1
valeur de retour de factorial(n→ 1) est 1 (recurse→ 1), puis
multipliée par 2 (result→ 2) : appel à factorial(n→ 2) terminé,
retourne 2
• valeur de retour de factorial(n→ 2) est 2 (recurse→ 2), puis multipliée
par 3 (result→ 6) : appel à factorial(n→ 3) terminé, retourne 6
Programmation (Chap. 9) Récursivité 18 / 33
Concept de récursivité
Analyse du flot d’exécution lors de l’appel factorial(3)• appel à factorial(n→ 3) : comme n �= 0, appel à factorial(n→ 2)
• détail de l’appel à factorial(n→ 2) :
appel à factorial(n→ 2) : comme n �= 0, appel à factorial(n→ 1)
détail de l’appel à factorial(n→ 1) :
� appel à factorial(n→ 1) : comme n �= 0, appel à
factorial(n→ 0)
� détail de l’appel à factorial(n→ 0) :� appel à factorial(n→ 0) : comme n = 0, retourne 1
� valeur de retour de factorial(n→ 0) est 1 (recurse→ 1), puis
multipliée par 1 (result→ 1) : appel à factorial(n→ 1)
terminé, retourne 1
valeur de retour de factorial(n→ 1) est 1 (recurse→ 1), puis
multipliée par 2 (result→ 2) : appel à factorial(n→ 2) terminé,
retourne 2
• valeur de retour de factorial(n→ 2) est 2 (recurse→ 2), puis multipliée
par 3 (result→ 6) : appel à factorial(n→ 3) terminé, retourne 6
Programmation (Chap. 9) Récursivité 18 / 33
Concept de récursivité
Analyse du flot d’exécution lors de l’appel factorial(3)• appel à factorial(n→ 3) : comme n �= 0, appel à factorial(n→ 2)
• détail de l’appel à factorial(n→ 2) :
appel à factorial(n→ 2) : comme n �= 0, appel à factorial(n→ 1)
détail de l’appel à factorial(n→ 1) :
� appel à factorial(n→ 1) : comme n �= 0, appel à
factorial(n→ 0)
� détail de l’appel à factorial(n→ 0) :
� appel à factorial(n→ 0) : comme n = 0, retourne 1
� valeur de retour de factorial(n→ 0) est 1 (recurse→ 1), puis
multipliée par 1 (result→ 1) : appel à factorial(n→ 1)
terminé, retourne 1
valeur de retour de factorial(n→ 1) est 1 (recurse→ 1), puis
multipliée par 2 (result→ 2) : appel à factorial(n→ 2) terminé,
retourne 2
• valeur de retour de factorial(n→ 2) est 2 (recurse→ 2), puis multipliée
par 3 (result→ 6) : appel à factorial(n→ 3) terminé, retourne 6
Programmation (Chap. 9) Récursivité 18 / 33
Concept de récursivité
Analyse du flot d’exécution lors de l’appel factorial(3)• appel à factorial(n→ 3) : comme n �= 0, appel à factorial(n→ 2)
• détail de l’appel à factorial(n→ 2) :
appel à factorial(n→ 2) : comme n �= 0, appel à factorial(n→ 1)
détail de l’appel à factorial(n→ 1) :
� appel à factorial(n→ 1) : comme n �= 0, appel à
factorial(n→ 0)
� détail de l’appel à factorial(n→ 0) :� appel à factorial(n→ 0) : comme n = 0, retourne 1
� valeur de retour de factorial(n→ 0) est 1 (recurse→ 1), puis
multipliée par 1 (result→ 1) : appel à factorial(n→ 1)
terminé, retourne 1
valeur de retour de factorial(n→ 1) est 1 (recurse→ 1), puis
multipliée par 2 (result→ 2) : appel à factorial(n→ 2) terminé,
retourne 2
• valeur de retour de factorial(n→ 2) est 2 (recurse→ 2), puis multipliée
par 3 (result→ 6) : appel à factorial(n→ 3) terminé, retourne 6
Programmation (Chap. 9) Récursivité 18 / 33
Concept de récursivité
Analyse du flot d’exécution lors de l’appel factorial(3)• appel à factorial(n→ 3) : comme n �= 0, appel à factorial(n→ 2)
• détail de l’appel à factorial(n→ 2) :
appel à factorial(n→ 2) : comme n �= 0, appel à factorial(n→ 1)
détail de l’appel à factorial(n→ 1) :
� appel à factorial(n→ 1) : comme n �= 0, appel à
factorial(n→ 0)
� détail de l’appel à factorial(n→ 0) :� appel à factorial(n→ 0) : comme n = 0, retourne 1
� valeur de retour de factorial(n→ 0) est 1 (recurse→ 1), puis
multipliée par 1 (result→ 1) : appel à factorial(n→ 1)
terminé, retourne 1
valeur de retour de factorial(n→ 1) est 1 (recurse→ 1), puis
multipliée par 2 (result→ 2) : appel à factorial(n→ 2) terminé,
retourne 2
• valeur de retour de factorial(n→ 2) est 2 (recurse→ 2), puis multipliée
par 3 (result→ 6) : appel à factorial(n→ 3) terminé, retourne 6
Programmation (Chap. 9) Récursivité 18 / 33
Concept de récursivité
Analyse du flot d’exécution lors de l’appel factorial(3)• appel à factorial(n→ 3) : comme n �= 0, appel à factorial(n→ 2)
• détail de l’appel à factorial(n→ 2) :
appel à factorial(n→ 2) : comme n �= 0, appel à factorial(n→ 1)
détail de l’appel à factorial(n→ 1) :
� appel à factorial(n→ 1) : comme n �= 0, appel à
factorial(n→ 0)
� détail de l’appel à factorial(n→ 0) :� appel à factorial(n→ 0) : comme n = 0, retourne 1
� valeur de retour de factorial(n→ 0) est 1 (recurse→ 1), puis
multipliée par 1 (result→ 1) : appel à factorial(n→ 1)
terminé, retourne 1
valeur de retour de factorial(n→ 1) est 1 (recurse→ 1), puis
multipliée par 2 (result→ 2) : appel à factorial(n→ 2) terminé,
retourne 2
• valeur de retour de factorial(n→ 2) est 2 (recurse→ 2), puis multipliée
par 3 (result→ 6) : appel à factorial(n→ 3) terminé, retourne 6
Programmation (Chap. 9) Récursivité 18 / 33
Concept de récursivité
Analyse du flot d’exécution lors de l’appel factorial(3)• appel à factorial(n→ 3) : comme n �= 0, appel à factorial(n→ 2)
• détail de l’appel à factorial(n→ 2) :
appel à factorial(n→ 2) : comme n �= 0, appel à factorial(n→ 1)
détail de l’appel à factorial(n→ 1) :
� appel à factorial(n→ 1) : comme n �= 0, appel à
factorial(n→ 0)
� détail de l’appel à factorial(n→ 0) :� appel à factorial(n→ 0) : comme n = 0, retourne 1
� valeur de retour de factorial(n→ 0) est 1 (recurse→ 1), puis
multipliée par 1 (result→ 1) : appel à factorial(n→ 1)
terminé, retourne 1
valeur de retour de factorial(n→ 1) est 1 (recurse→ 1), puis
multipliée par 2 (result→ 2) : appel à factorial(n→ 2) terminé,
retourne 2
• valeur de retour de factorial(n→ 2) est 2 (recurse→ 2), puis multipliée
par 3 (result→ 6) : appel à factorial(n→ 3) terminé, retourne 6
Programmation (Chap. 9) Récursivité 18 / 33
Concept de récursivité
Analyse du flot d’exécution lors de l’appel factorial(3)
Diagramme de pile :
factorial
factorial
factorial
factorial
1
1
2
6
6
n
recurse
result 2
1
2
2
n
recurse
result 1
1
1
3
n 0
n
recurse
result
Note : recurse et result n’existent pas dans le dernier appel car leur
portée est limitée au else
Programmation (Chap. 9) Récursivité 19 / 33
Concept de récursivité
Récursivité et flot d’exécution
Problème
Soit la fonction récursive suivante, comment prédire ce qu’elle va
faire ? Essayez de décrire le flot d’exécution après un appel à
post_count(3).
>>> def post_count(n):if n <= 0:
print ’Boum’else:
print npost_count(n - 1)
>>> post_count(3)
Programmation (Chap. 9) Récursivité 20 / 33
Concept de récursivité
Analyse du flot d’exécution lors de l’appel post_count(3)
• appel à post_count avec n→ 3 : comme n est plus grand que 0, affiche la
valeur 3, puis appelle post_count avec n→ 2
• détail de l’appel à post_count avec n→ 2 :
appel à post_count avec n→ 2 : comme n est plus grand que 0,
affiche la valeur 2, puis appelle post_count avec n→ 1
détail de l’appel à post_count avec n→ 1 :
� appel à post_count avec n→ 1 : comme n est plus grand que 0,
affiche la valeur 1, puis appelle post_count avec n→ 0
� détail de l’appel à post_count avec n→ 0 :
� appel à post_count avec n→ 0 : comme n n’est pas plus grand
que 0, affiche le mot ‘Boum’ et se termine
� l’appel à post_count avec n→ 1 est terminé
l’appel à post_count avec n→ 2 est terminé
• l’appel à post_count avec n→ 3 est terminé
Programmation (Chap. 9) Récursivité 21 / 33
Concept de récursivité
Analyse du flot d’exécution lors de l’appel post_count(3)
• appel à post_count avec n→ 3 : comme n est plus grand que 0, affiche la
valeur 3, puis appelle post_count avec n→ 2
• détail de l’appel à post_count avec n→ 2 :
appel à post_count avec n→ 2 : comme n est plus grand que 0,
affiche la valeur 2, puis appelle post_count avec n→ 1
détail de l’appel à post_count avec n→ 1 :
� appel à post_count avec n→ 1 : comme n est plus grand que 0,
affiche la valeur 1, puis appelle post_count avec n→ 0
� détail de l’appel à post_count avec n→ 0 :
� appel à post_count avec n→ 0 : comme n n’est pas plus grand
que 0, affiche le mot ‘Boum’ et se termine
� l’appel à post_count avec n→ 1 est terminé
l’appel à post_count avec n→ 2 est terminé
• l’appel à post_count avec n→ 3 est terminé
Programmation (Chap. 9) Récursivité 21 / 33
Concept de récursivité
Analyse du flot d’exécution lors de l’appel post_count(3)
• appel à post_count avec n→ 3 : comme n est plus grand que 0, affiche la
valeur 3, puis appelle post_count avec n→ 2
• détail de l’appel à post_count avec n→ 2 :
appel à post_count avec n→ 2 : comme n est plus grand que 0,
affiche la valeur 2, puis appelle post_count avec n→ 1
détail de l’appel à post_count avec n→ 1 :
� appel à post_count avec n→ 1 : comme n est plus grand que 0,
affiche la valeur 1, puis appelle post_count avec n→ 0
� détail de l’appel à post_count avec n→ 0 :
� appel à post_count avec n→ 0 : comme n n’est pas plus grand
que 0, affiche le mot ‘Boum’ et se termine
� l’appel à post_count avec n→ 1 est terminé
l’appel à post_count avec n→ 2 est terminé
• l’appel à post_count avec n→ 3 est terminé
Programmation (Chap. 9) Récursivité 21 / 33
Concept de récursivité
Analyse du flot d’exécution lors de l’appel post_count(3)
• appel à post_count avec n→ 3 : comme n est plus grand que 0, affiche la
valeur 3, puis appelle post_count avec n→ 2
• détail de l’appel à post_count avec n→ 2 :
appel à post_count avec n→ 2 : comme n est plus grand que 0,
affiche la valeur 2, puis appelle post_count avec n→ 1
détail de l’appel à post_count avec n→ 1 :
� appel à post_count avec n→ 1 : comme n est plus grand que 0,
affiche la valeur 1, puis appelle post_count avec n→ 0
� détail de l’appel à post_count avec n→ 0 :� appel à post_count avec n→ 0 : comme n n’est pas plus grand
que 0, affiche le mot ‘Boum’ et se termine
� l’appel à post_count avec n→ 1 est terminé
l’appel à post_count avec n→ 2 est terminé
• l’appel à post_count avec n→ 3 est terminé
Programmation (Chap. 9) Récursivité 21 / 33
Concept de récursivité
Analyse du flot d’exécution lors de l’appel post_count(3)
L’ordre des instructions d’affichage de l’analyse précédente explique
pourquoi on obtient :
>>> post_count(3)
321
Boum
Programmation (Chap. 9) Récursivité 22 / 33
Concept de récursivité
Récursivité et flot d’exécution
Problème
Que se passe-t-il si on modifie légèrement la fonction précédente en
déplaçant l’appel récursif avant l’instruction “print n” ?
>>> def pre_count(n):if n <= 0:
print ’Boum’else:
pre_count(n - 1)print n
>>> pre_count(3)
Programmation (Chap. 9) Récursivité 23 / 33
Concept de récursivité
Récursivité et flot d’exécution
Pour résoudre ce problème, on peut refaire l’analyse du flot détaillée,
mais ce n’est pas nécessaire si l’on “accepte”1
que la récursivité
fonctionne et qu’on lit le corps du else comme suit :
Etape de recurrence:pre_count(n - 1)print n
Ce qui s’interprète par
on réalise d’abord le travail pour les valeurs plus petites que n
ensuite on affiche n
>>> pre_count(3)
Boum123
1C’est ce que Downey appelle le “leap of faith” (acte de foi).
Programmation (Chap. 9) Récursivité 24 / 33
Concept de récursivité
Définitions récursives
La récursivité est très naturelle pour résoudre des problèmes qui sont
définis récursivement.
Par exemple, la séquence des nombres de Fibonacci est définie
mathématiquement de manière récursive :
F0 = 0,F1 = 1,Fn = Fn−1 +Fn−2, ∀n ≥ 2.
Programmation (Chap. 9) Récursivité 25 / 33
Concept de récursivité
Définitions récursives
En Python :
def fibo(n):if n == 0:
return 0elif n == 1:
return 1else:
return fibo(n - 1) + fibo(n - 2)
Programmation (Chap. 9) Récursivité 26 / 33
Concept de récursivité
Fibonacci amélioré
Retour sur Fibonacci : les dictionnaires permettent d’écrire le calcul de
Fibonacci récursivement, tout en utilisant le principe de la version
itérative : pour éviter de recalculer un grand nombre de fois les mêmes
valeurs, on les stocke en mémoire.
known = {0 : 0, 1 : 1}
def fib_dict(n):if n in known:
return known[n]res = fib_dict(n-1) + fib_dict(n-2)known[n] = resreturn res
Remarque : définir known en dehors de toute fonction, permet d’y
accéder dans toutes les fonctions (variable “globale”).
Programmation (Chap. 9) Récursivité 27 / 33
Concept de récursivité
Récursion infinie
Si une récursion n’atteint jamais un cas de base, le programme ne se
termine (théoriquement) jamais ! C’est une récursion infinie.
def recurse():recurse()
→ voir IDLE pour le comportement de cette fonction
Important de considérer les cas de bases et le fait que chaque appel
récursif doit s’approcher d’un cas de base.
Programmation (Chap. 9) Récursivité 28 / 33
Concept de récursivité
Récursion infinie
En réalité, la récursion infinie n’existe (heureusement) pas, car une
exception est lancée quand le nombre d’appels récursif dépasse une
certaine limite. On peut la connaître (et la modifier) grâce à certaines
fonctions du module sys.
>>> import sys>>> print sys.getrecursionlimit()
1000>>> sys.setrecursionlimit(10000)
Si cette limite n’était pas définie, le programme “planterait” une fois
que la mémoire allouée sur la machine pour appeler les milliers
d’appels récursifs serait dépassée, ce qui serait bien plus gênant
qu’une exception (gérable, voir plus tard).
Programmation (Chap. 9) Récursivité 29 / 33
Concept de récursivité
Récursion infinie
Que se passe-t-il si on appelle factorial avec 1.5 comme
argument ?
>>> sys.setrecursionlimit(10000)>>> factorial(1.5)
...
RuntimeError: maximum recursion depth exceeded
La base (n == 0) n’est jamais atteinte :
1.5 → 0.5 → -0.5 → -1.5 → ...
Programmation (Chap. 9) Récursivité 30 / 33
Concept de récursivité
Tester les types
Pour éviter ce genre de problèmes, on peut utiliser la fonction
booléenne isinstance qui vérifie si un argument est d’un type donné.
>>> def factorial(n):if not isinstance(n, int):
print ’Factorial is only defined for integers.’return None
elif n < 0:print ’Factorial is only defined for positive integers.’return None
elif n == 0:return 1
else:return n * factorial(n - 1)
>>> factorial(’fred’)
Factorial is only defined for integers.None>>> factorial(-2)Factorial is only defined for positive integers.None
Programmation (Chap. 9) Récursivité 31 / 33
Glossaire
Glossaire
Programmation (Chap. 9) Récursivité 32 / 33
Glossaire
Glossaire
récursivité : le fait pour une fonction de s’appeler elle-même
cas de base : branche conditionnelle dans une fonction récursive qui ne fait pas d’appel
récursif
récursion infinie : fonction qui s’appelle elle-même indéfiniment (sans atteindre jamais un
cas de base). Provoque une exception.
variable globale : variable définie en dehors de toute fonction, sa portée est globale.
Programmation (Chap. 9) Récursivité 33 / 33
Chapitre 10. Prouver les propriétés des algorithmes
Contenu du chapitre
1 Quelques algorithmes
2 Preuve d’arrêt d’un algorithme
3 La notion d’invariant de boucle
4 Preuve d’exactitude d’un algorithme itératif
5 Preuve d’exactitude d’un algorithme récursif
Programmation (Chap. 10) Prouver les propriétés des algorithmes 1 / 48
Quelques algorithmes
Quelques algorithmes
Programmation (Chap. 10) Prouver les propriétés des algorithmes 2 / 48
Quelques algorithmes
Preuves des propriétés des algorithmes
Etant donné un (morceau) d’algorithme ou de programme itératif ou
récursif, on désire prouver que si si les préconditions sont
respectées. :
l’arrêt de l’algorithme : prouver qu’il n’y a pas de boucle ou de
récursion infinie, que le programme s’arrête toujours.
l’exactitude de l’algorithme : prouver que l’algorithme fait bien ce
qu’il est supposé faire, que les postconditions sont respectées.
Nous allons présenter quelques algorithmes et prouver leurs
propriétés (preuves par récurrence).
Programmation (Chap. 10) Prouver les propriétés des algorithmes 3 / 48
Quelques algorithmes
Algorithme : division euclidienne par soustraction
Problème
Soit a et b deux entiers tels que a≥ 0 et b > 0. Comment déterminer
le quotient (entier) et le reste de la division de a par b ?
Idée de l’algorithme de division euclidienne par soustraction.
Initialiser le reste à a. Ensuite, soustraire b au reste tant que celui-ci
est plus grand que b. Le nombre de soustraction est égal au quotient.
Exemple : Soit a = 14 et b = 5.
# soustractions reste
0 14
1 9
2 4
Le quotient deab est 2 et le reste est 4.
Programmation (Chap. 10) Prouver les propriétés des algorithmes 4 / 48
Quelques algorithmes
Algorithme : division euclidienne par soustraction
Fonction en Python :
1 def division(a, b):2 r = a3 q = 04 while r >= b:5 r = r - b6 q = q + 17 return (q, r)
>>> division(14, 5)(2, 4)>>> 14 / 52>>> 14 % 54
Programmation (Chap. 10) Prouver les propriétés des algorithmes 5 / 48
Quelques algorithmes
Algorithme : division euclidienne par soustraction
A prouver : supposons que a et b soient deux entiers tels que a≥ 0 et
b > 0,
la fonction division s’arrête toujours ? (preuve de l’arrêt)
après l’exécution de la fonction division les valeurs retournées
correspondent-elles bien au quotient et au reste deab ? (preuve
de l’exactitude)
Programmation (Chap. 10) Prouver les propriétés des algorithmes 6 / 48
Quelques algorithmes
Algorithme : nombres de Fibonacci
Fonction en Python :
1 def fibo(n):2 if n == 0:3 return 04 elif n == 1:5 return 16 else:7 return fibo(n-1) + fibo(n-2)
>>> for i in range(10):print fibo(i),
0 1 1 2 3 5 8 13 21 34
Programmation (Chap. 10) Prouver les propriétés des algorithmes 7 / 48
Quelques algorithmes
Algorithme : nombres de Fibonacci
A prouver : supposons que n soit un entier tel que n ≥ 0,
la fonction fibo s’arrête toujours ?
après l’exécution de la fonction fibo, la valeur retournée
correspond-elle bien au nième nombre de Fibonacci ?
Programmation (Chap. 10) Prouver les propriétés des algorithmes 8 / 48
Quelques algorithmes
Algorithme : calcul de l’exposant
Problème
Soit a un nombre réel et n un entier ≥ 0. Comment calculer an?
Idée : au lieu de calculer a×a× . . .×a, on va utiliser utiliser l’idée
suivante qui devrait effectuer beaucoup moins de multiplications.
Si n est pair, alors pour calculer xn, on remplace x par x2
et n parn2;
Si n est impair, alors on calcule x · xn−1dont le deuxième terme
nous ramène au cas pair.
Exemple : Pour calculer x9, on va calculer
x9 =��
x2�2
�2
· x ,
ce qui revient à faire 4 multiplications (au lieu de 8).
Programmation (Chap. 10) Prouver les propriétés des algorithmes 9 / 48
Quelques algorithmes
Algorithme : calcul de l’exposant
Fonction en Python :
1 def expo(a, n):2 r = 13 while n > 0:4 if n % 2 == 0:5 a = a * a6 n = n / 27 else:8 r = r * a9 n = n - 1
10 return r
>>> expo(3,3)27>>> expo(2,9)512>>> expo(2,0)1
Programmation (Chap. 10) Prouver les propriétés des algorithmes 10 / 48
Quelques algorithmes
Algorithme : calcul de l’exposant
A prouver : supposons que a soit un réel et n un entier ≥ 0,
la fonction expo s’arrête toujours ?
après l’exécution de la fonction expo la valeur retournée
correspond-elle bien à an?
Programmation (Chap. 10) Prouver les propriétés des algorithmes 11 / 48
Quelques algorithmes
Algorithme : tri par sélection
Problème
Soit une séquence de n entiers (n ≥ 0), comment trier (par ordre
croissant) les éléments de la séquence ?
Idée du tri par sélection.
Imaginons que nous devions trier une main
d’un jeu de cartes. On pose les cartes faces
visibles sur la table. On sélectionne la plus
“petite” carte et on la prend en main. On
sélectionne ensuite la plus petite carte parmi
celles restées sur la table et on la place à
droite de la carte en main. On répète le
processus jusqu’à avoir sélectionné toutes les
cartes posées sur la table.
Programmation (Chap. 10) Prouver les propriétés des algorithmes 12 / 48
Quelques algorithmes
Algorithme : tri par sélection
Idée du tri par sélection.
Pour appliquer cette idée à une séquence d’entiers, on représente la
séquence comme suit : la partie grisée est la partie triée de la
séquence (les cartes en main) et la partie blanche représente la partie
non triée (les cartes sur la table).
7
1 7 6 5 3 4
1 2 3 6 5 7 4
1 3 4 5 7 6
1 2 3 4 5 6
1 2 3 4 6 7
2
1 4
1 7 2 6 5 3 4
3 7 2 6 5
2
5
sélectionner une valeur revient à
échanger la première valeur non
triée avec la plus petite valeur non
triée (avec les cartes cela revient à
les trier par échanges successifs en
les laissant sur la table)
quand il ne reste plus qu’un élément
à trier, on peut s’arrêter
deux boucles nécessaires
(voyez-vous pourquoi ?)
Programmation (Chap. 10) Prouver les propriétés des algorithmes 13 / 48
Quelques algorithmes
Algorithme : tri par sélection
Idée du tri par sélection.
Pour appliquer cette idée à une séquence d’entiers, on représente la
séquence comme suit : la partie grisée est la partie triée de la
séquence (les cartes en main) et la partie blanche représente la partie
non triée (les cartes sur la table).
7
1 7 6 5 3 4
1 2 3 6 5 7 4
1 3 4 5 7 6
1 2 3 4 5 6
1 2 3 4 6 7
2
1 4
1 7 2 6 5 3 4
3 7 2 6 5
2
5
sélectionner une valeur revient à
échanger la première valeur non
triée avec la plus petite valeur non
triée (avec les cartes cela revient à
les trier par échanges successifs en
les laissant sur la table)
quand il ne reste plus qu’un élément
à trier, on peut s’arrêter
deux boucles nécessaires
(voyez-vous pourquoi ?)
Programmation (Chap. 10) Prouver les propriétés des algorithmes 13 / 48
Quelques algorithmes
Algorithme : tri par sélection
Une première boucle permet de sélectionner chaque élément un par
un : quand l’indice de la première boucle vaut i , les i premiers
éléments sont triés (indices 0 à i−1). Appelons small l’indice de la
plus petite valeur des éléments non triés. Une deuxième boucle est
nécessaire pour le déterminer.
......... 1 7 6 5 3 42
1 3 4 5 620
i
1 3 4 5 7 62
1 3 4 5 620
ismall small
Programmation (Chap. 10) Prouver les propriétés des algorithmes 14 / 48
Quelques algorithmes
Algorithme : tri par sélection
Fonction en Python :
1 def selection_sort(t):2 n = len(t)3 for i in range(n-1):4 small = i5 for j in range(i+1,n):6 if t[j] < t[small]:7 small = j8 (t[i], t[small]) = (t[small], t[i])
>>> t = [3, 7, 2, 6, 5, 1, 4]>>> selection_sort(t)>>> t[1, 2, 3, 4, 5, 6, 7]
Programmation (Chap. 10) Prouver les propriétés des algorithmes 15 / 48
Quelques algorithmes
Algorithme : tri par sélection
A prouver : supposons que A soit une séquence de n entiers (n ≥ 0),
la fonction selection_sort s’arrête toujours ?
après l’exécution de la fonction selection_sort les éléments de
A sont-ils triés par ordre croissant ?
Programmation (Chap. 10) Prouver les propriétés des algorithmes 16 / 48
Preuve d’arrêt d’un algorithme
Preuve d’arrêt d’un algorithme
Programmation (Chap. 10) Prouver les propriétés des algorithmes 17 / 48
Preuve d’arrêt d’un algorithme
Preuve d’arrêt d’un algorithme
Pour prouver qu’un algorithme s’arrête (si les préconditions sont
respectées), il faut prouver que
chaque boucle se termine en un nombre fini d’itérations.
chaque fonction récursive atteint un cas de base en un nombre
fini d’étapes.
Remarque : en fonction du problème, ce n’est pas toujours facile (ou
possible) d’écrire une preuve d’arrêt (cf. chap. 6 et le calcul de la suite
de Syracuse).
Programmation (Chap. 10) Prouver les propriétés des algorithmes 18 / 48
Preuve d’arrêt d’un algorithme
Preuve d’arrêt d’un algorithme itératif : boucle while
Exemple : Nous avons déjà prouvé (cf. chap. 6) que la boucle while
suivante s’arrête quand n est un entier ≥ 0 fini.
1 def countdown(n):2 while n > 0:3 print n4 n = n - 15 print ’Boum’
Preuve de l’arrêt de countdown (version itérative).
Base. On suppose que n est un entier ≥ 0 (fini). Si n = 0, la
condition (2) est évaluée à faux, et la boucle se termine.
Réc. Supposons que la boucle s’arrête pour n ≤ k (k ≥ 0), et
prouvons le pour n = k +1. Quand n = k +1, l’instruction 3 est
exécutée (affiche k +1), puis n = k lors de l’instruction 4.
La boucle est répétée avec n = k . Celle-ci s’arrêtera par
hypothèse de récurrence.
Programmation (Chap. 10) Prouver les propriétés des algorithmes 19 / 48
Preuve d’arrêt d’un algorithme
Preuve d’arrêt d’un algorithme itératif : boucle while
Problème
Prouver que la division euclidienne par soustraction s’arrête quand
a≥ 0 et b > 0.
1 def division(a, b):2 r = a3 q = 04 while r >= b:5 r = r - b6 q = q + 17 return (q, r)
Preuve.
Au tableau.
Programmation (Chap. 10) Prouver les propriétés des algorithmes 20 / 48
Preuve d’arrêt d’un algorithme
Preuve d’arrêt d’un algorithme itératif : boucle while
Problème
Prouver que le calcul de l’exposant s’arrête quand n ≥ 0.
1 def expo(a, n):2 r = 13 while n > 0:4 if n % 2 == 0:5 a = a * a6 n = n / 27 else:8 r = r * a9 n = n - 1
10 return r
Preuve.
Au tableau.
Programmation (Chap. 10) Prouver les propriétés des algorithmes 21 / 48
Preuve d’arrêt d’un algorithme
Preuve d’arrêt d’un algorithme itératif : boucle for
Prouver qu’une boucle for s’arrête est très simple si celle-ci est
appliquée sur une séquence :
si la boucle itère sur chaque élément de la séquence (une et une
seule fois);
si la séquence est finie;
si le corps de la boucle ne modifie pas le nombre d’éléments de
la séquence;
alors il est évident que la boucle se termine en un nombre fini
d’étapes.
Programmation (Chap. 10) Prouver les propriétés des algorithmes 22 / 48
Preuve d’arrêt d’un algorithme
Preuve d’arrêt d’un algorithme itératif : boucle for
En Python, les deux premiers points sont automatiquement gérés par
la syntaxe d’une boucle for et par le fait qu’une séquence est toujours
finie.
Si le corps de la boucle ne modifie pas (n’augmente pas) le nombre
d’éléments de la séquence, la preuve de l’arrêt est immédiate.
1 def sum_all(liste):2 res = 03 for item in liste:4 res += item5 return res
Remarque : pour le moment nous travaillons avec des séquences finies,
donc ce qui est dit ici est correct, mais il existe une notion de générateurs qui
permettent de générer des séquences infinies. Dans ce cas, la preuve d’arrêt
doit être faite.
Programmation (Chap. 10) Prouver les propriétés des algorithmes 23 / 48
Preuve d’arrêt d’un algorithme
Preuve d’arrêt d’un algorithme itératif : boucle for
La fonction suivante prend une liste d’entiers en argument et modifie la
liste de telle sorte que chaque élément est suivi par son carré.
1 def add_square(liste):2 index = 03 for item in liste:4 if index % 2 == 0:5 liste.insert(index+1, item*item)6 index = index + 1
>>> t = [1, 2, 3]>>> add_square(t)>>> t[1, 1, 2, 4, 3, 9]
Ici une preuve serait nécessaire, mais cette fonction peut être réécrite
de telle sorte qu’une preuve est inutile (voir slide suivant). Ce genre de
code est à éviter !
Programmation (Chap. 10) Prouver les propriétés des algorithmes 24 / 48
Preuve d’arrêt d’un algorithme
Preuve d’arrêt d’un algorithme itératif : boucle for
On construit une nouvelle liste et la preuve devient inutile. On retourne
la liste modifiée plutôt que de la transformer dans la fonction.
1 def add_square(liste):2 res = []3 for item in liste:4 res.extend([item, item * item])5 return res
>>> t = [1, 2, 3]>>> t = add_square(t)>>> t[1, 1, 2, 4, 3, 9]
Programmation (Chap. 10) Prouver les propriétés des algorithmes 25 / 48
Preuve d’arrêt d’un algorithme
Preuve d’arrêt d’un algorithme itératif
S’il y a plusieurs boucles, il faut prouver que chacune d’elles s’arrêtent.
Problème
Prouver que le tri par sélection s’arrête.
1 def selection_sort(t):2 n = len(t)3 for i in range(n-1):4 small = i5 for j in range(i+1,n):6 if t[j] < t[small]:7 small = j8 (t[i], t[small]) = (t[small], t[i])
Preuve.
On observe d’abord que, sous l’hypothèse qu’un indice i < n−1 est
fixé, la boucle for (instr. 5 – 7) s’arrête, car elle s’applique sur une
séquence finie. Ensuite, on observe que la boucle for (instr. 3 – 8)
s’applique sur une séquence finie et s’arrête également.
Programmation (Chap. 10) Prouver les propriétés des algorithmes 26 / 48
Preuve d’arrêt d’un algorithme
Preuve d’arrêt d’un algorithme récursif
Prouver qu’un algorithme récursif s’arrête est naturel : on utilise une
preuve par récurrence dont les cas de base et récursifs correspondent
à leurs équivalents dans l’algorithme.
1 def countdown(n):2 if n == 0:3 print ’Boum’4 else:5 print n6 countdown(n-1)
Preuve de l’arrêt de countdown (version récursive).
Base. On suppose que n est un entier ≥ 0 (fini). Si n = 0, la condition
(2) est évaluée à vrai, et il fonction s’arrête car il n’y a pas d’appels
récursifs.
Réc. Supposons que la fonction s’arrête pour n ≤ k (k ≥ 0), et
prouvons le pour n = k +1. Quand n = k +1 > 0, l’instruction 5 est
exécutée (affiche k +1), puis il y a un appel récursif sur n = k lors de
l’instruction 6. Celui-ci s’arrêtera par hypothèse de récurrence.
Programmation (Chap. 10) Prouver les propriétés des algorithmes 27 / 48
Preuve d’arrêt d’un algorithme
Preuve d’arrêt d’un algorithme récursif
Problème
Prouver que le calcul du nième nombre de Fibonacci s’arrête toujours
si n ≥ 0
1 def fibo(n):2 if n == 0:3 return 04 elif n == 1:5 return 16 else:7 return fibo(n-1) + fibo(n-2)
Preuve.
Au tableau.
Programmation (Chap. 10) Prouver les propriétés des algorithmes 28 / 48
La notion d’invariant de boucle
La notion d’invariant de boucle
Programmation (Chap. 10) Prouver les propriétés des algorithmes 29 / 48
La notion d’invariant de boucle
Invariant de boucle
Pour prouver l’exactitude d’un algorithme itératif, on utilise la notion
d’invariant de boucle.
Un invariant de boucle est une assertion (une propriété) qui
est vraie avant l’entrée dans la boucle;
est toujours vraie au début de chaque itération de la boucle.
Remarques :
cela implique que si une boucle s’arrête, l’invariant est toujours
vrai après l’exécution complète de celle-ci;
l’invariant ne doit pas être vérifié à tout moment dans le corps de
la boucle, mais au début de chaque itération de celle-ci.
Programmation (Chap. 10) Prouver les propriétés des algorithmes 30 / 48
La notion d’invariant de boucle
Invariant de boucle
La preuve complète qu’un algorithme itératif est exact revient à :
montrer qu’il s’arrête
à exhiber, pour chaque boucle, une propriété (invariant de boucle)
qui, si elle est valide avant l’exécution d’un tour de boucle, est
aussi valide après l’exécution du tour de boucle; et la prouver
vérifier que les conditions initiales rendent la propriété vraie en
entrée du premier tour de boucle
conclure que cette propriété est vraie en sortie du dernier tour de
boucle; un bon choix de la propriété prouvera qu’on a bien
produit le résultat souhaité.
La principale difficulté de ce type de preuve réside dans la
détermination de l’invariant de boucle.
Programmation (Chap. 10) Prouver les propriétés des algorithmes 31 / 48
La notion d’invariant de boucle
Invariant de boucle
Exemple : Dans la division euclidienne par soustraction, l’assertion
a = b×q + r , (1)
est un invariant de boucle.
1 def division(a, b):2 r = a3 q = 04 while r >= b:5 r = r - b6 q = q + 17 return (q, r)
a b q r b×q + r = a14 5 0 14 5×0+14 = 14
14 5 1 9 5×1+9 = 14
14 5 2 4 5×2+4 = 14
Programmation (Chap. 10) Prouver les propriétés des algorithmes 32 / 48
La notion d’invariant de boucle
Invariant de boucle
Calcul de l’exposant :
1 def expo(a, n):2 r = 13 while n > 0:4 if n % 2 == 0:5 a = a * a6 n = n / 27 else:8 r = r * a9 n = n - 1
10 return r
→
1 def expo(a, n):2 r = 13 b = a4 i = n5 while i > 0:6 if i % 2 == 0:7 b = b * b8 i = i / 29 else:
10 r = r * b11 i = i - 112 return r
Programmation (Chap. 10) Prouver les propriétés des algorithmes 33 / 48
La notion d’invariant de boucle
Invariant de boucle
Il n’est pas toujours facile de déterminer un invariant. Dans le calcul
de l’exposant, l’assertion
??? (exercice) (2)
est un invariant de boucle.
1 def expo(a, n):2 r = 13 b = a4 i = n5 while i > 0:6 if i % 2 == 0:7 b = b * b8 i = i / 29 else:
10 r = r * b11 i = i - 112 return r
Soit a = 2 et n = 9, alors an = 512.
r b i r ×bi
1 2 9 1×29 = 512
2 2 8 2×28 = 512
2 4 4 2×44 = 512
2 16 2 2×162 = 512
2 256 1 2×2561 = 512
512 256 0 512×2560 = 512
Programmation (Chap. 10) Prouver les propriétés des algorithmes 34 / 48
La notion d’invariant de boucle
Invariant de boucle
Il n’est pas toujours facile de déterminer un invariant. Dans le calcul
de l’exposant, l’assertion
an = r ×bi , (2)
est un invariant de boucle.
1 def expo(a, n):2 r = 13 b = a4 i = n5 while i > 0:6 if i % 2 == 0:7 b = b * b8 i = i / 29 else:
10 r = r * b11 i = i - 112 return r
Soit a = 2 et n = 9, alors an = 512.
r b i r ×bi
1 2 9 1×29 = 512
2 2 8 2×28 = 512
2 4 4 2×44 = 512
2 16 2 2×162 = 512
2 256 1 2×2561 = 512
512 256 0 512×2560 = 512
Programmation (Chap. 10) Prouver les propriétés des algorithmes 34 / 48
La notion d’invariant de boucle
Invariant de boucle
Tri par sélection :
1 def selection_sort(t):2 n = len(t)3 for i in range(n-1):4 small = i5 for j in range(i+1,n):6 if t[j] < t[small]:7 small = j8 (t[i], t[small]) = (t[small], t[i])
Invariants des boucles ?
Boucle intérieure : La valeur de small est l’indice du plus petit élément
de la sous-séquence t[i], . . . , t[j−1].Boucle extérieure : La sous-séquence t[0], . . . , t[i − 1] est triée, c-à-d
t[0] ≤ t[1] ≤ . . . ≤ t[i − 1] et toutes les valeurs de la sous-séquence
t[i], . . . , t[n− 1] sont supérieures ou égales à n’importe laquelle des
valeurs dans t[0], . . . , t[i−1].
Programmation (Chap. 10) Prouver les propriétés des algorithmes 35 / 48
La notion d’invariant de boucle
Invariant de boucle
Tri par sélection :
1 def selection_sort(t):2 n = len(t)3 for i in range(n-1):4 small = i5 for j in range(i+1,n):6 if t[j] < t[small]:7 small = j8 (t[i], t[small]) = (t[small], t[i])
Invariants des boucles ?
Boucle intérieure : La valeur de small est l’indice du plus petit élément
de la sous-séquence t[i], . . . , t[j−1].
Boucle extérieure : La sous-séquence t[0], . . . , t[i − 1] est triée, c-à-d
t[0] ≤ t[1] ≤ . . . ≤ t[i − 1] et toutes les valeurs de la sous-séquence
t[i], . . . , t[n− 1] sont supérieures ou égales à n’importe laquelle des
valeurs dans t[0], . . . , t[i−1].
Programmation (Chap. 10) Prouver les propriétés des algorithmes 35 / 48
La notion d’invariant de boucle
Invariant de boucle
Tri par sélection :
1 def selection_sort(t):2 n = len(t)3 for i in range(n-1):4 small = i5 for j in range(i+1,n):6 if t[j] < t[small]:7 small = j8 (t[i], t[small]) = (t[small], t[i])
Invariants des boucles ?
Boucle intérieure : La valeur de small est l’indice du plus petit élément
de la sous-séquence t[i], . . . , t[j−1].Boucle extérieure : La sous-séquence t[0], . . . , t[i − 1] est triée, c-à-d
t[0] ≤ t[1] ≤ . . . ≤ t[i − 1] et toutes les valeurs de la sous-séquence
t[i], . . . , t[n− 1] sont supérieures ou égales à n’importe laquelle des
valeurs dans t[0], . . . , t[i−1].
Programmation (Chap. 10) Prouver les propriétés des algorithmes 35 / 48
Preuve d’exactitude d’un algorithme itératif
Preuve d’exactitude d’un
algorithme itératif
Programmation (Chap. 10) Prouver les propriétés des algorithmes 36 / 48
Preuve d’exactitude d’un algorithme itératif
Preuve d’exactitude d’un algorithme itératif
Supposons que nous ayons prouvé qu’un algorithme itératif s’arrête,
prouver qu’il est exact revient à :
déterminer un invariant pour chaque boucle;
prouver les invariants de boucles;
utiliser l’invariant (et les valeurs des variables) au sortir de la
boucle pour vérifier l’exactitude du résultat final.
Pour prouver un invariant de boucle :
on prouve l’assertion avant d’entrer dans la boucle;
on suppose l’assertion vraie pour k−1≥ 1 premières itérations
de la boucle et l’on prouve que c’est toujours vrai après la k ième
itération;
en pratique, on fait la distinction des valeurs pour chaque variable
avant et après une exécution d’une itération de la boucle :
notations aold , anew .
Programmation (Chap. 10) Prouver les propriétés des algorithmes 37 / 48
Preuve d’exactitude d’un algorithme itératif
Preuve d’exactitude d’un algorithme itératif
Exemple : division euclidienne.
1 def division(a, b):2 r = a3 q = 04 while r >= b:5 r = r - b6 q = q + 17 return (q, r)
Les variables q et r étant modifiées dans la boucle, on utilise les
notations, pour une itération donnée :
qold et rold : valeurs de q et r au début de l’itération;
qnew et rnew : valeurs de q et r à la fin de l’itération;
Programmation (Chap. 10) Prouver les propriétés des algorithmes 38 / 48
Preuve d’exactitude d’un algorithme itératif
Preuve d’exactitude d’un algorithme itératif
A prouver : a = b×q + r est un invariant de boucle.
Preuve.
Avant d’entrer dans la boucle : q est initialisé à 0 et r à a, donc
b×q + r = b×0+a = a et l’assertion est vraie.
Par hypothèse, a = b×qold + rold . Considérons une itération pour
vérifier que a = b×qnew + rnew est toujours vrai. On a rnew = rold −b et
qnew = qold +1 par les instructions 5 et 6. Donc,
a = b×qold +rold = b×(qnew −1)+(rnew +b) = b×qnew +rnew +b−b,
ce qui donne a = b×qnew + rnew après l’exécution de l’itération.
Programmation (Chap. 10) Prouver les propriétés des algorithmes 39 / 48
Preuve d’exactitude d’un algorithme itératif
Preuve d’exactitude d’un algorithme itératif
La preuve complète de l’exactitude de la fonction division est donc :
preuve de l’arrêt (voir avant)
preuve que a = b×q + r est un invariant de la boucle (voir slide
précédent)
le résultat retourné est correct car :
� la boucle se termine quand la condition est fausse, c-à-d, quand
r < b (= négation de la condition);
� à ce moment là, a = b×q + r est vrai, donc, q = a−rb et r < b, ce
qui correspond à la définition du quotient et du reste d’une
division1.
1Ici nous utilisons le fait (sans le prouver, cf. cours de Mathématiques
élémentaires) qu’il existe un couple unique d’entiers q et r tels que a = b×q + r et
r < b.
Programmation (Chap. 10) Prouver les propriétés des algorithmes 40 / 48
Preuve d’exactitude d’un algorithme itératif
Preuve d’exactitude d’un algorithme itératif
Calcul de l’exposant.
1 def expo(a, n):2 r = 13 b = a4 i = n5 while i > 0:6 if i % 2 == 0:7 b = b * b8 i = i / 29 else:
10 r = r * b11 i = i - 112 return r
Preuve de l’exactitude de expo.
Au tableau.
Programmation (Chap. 10) Prouver les propriétés des algorithmes 41 / 48
Preuve d’exactitude d’un algorithme itératif
Preuve d’exactitude d’un algorithme itératif
Le fait qu’une boucle for s’applique sur une séquence peut nécessiter
d’exprimer l’invariant sous la forme d’une assertion S(k) où kreprésente le nombre d’itérations (= le nombre d’éléments) sur
lesquelles la boucle a été exécutée.
1 def sum_all(liste):2 res = 03 for item in liste:4 res += item5 return res
cas de base S(0) : avant d’entrer dans la boucle;
cas récursif : si S(k) est vrai, preuve de S(k +1);
si n est le nombre d’éléments de la séquence, alors S(n)représente l’invariant après l’exécution complète de la boucle;
besoin d’identifier la relation entre k et les variables utilisées
dans la boucle (par ex. indice).
Programmation (Chap. 10) Prouver les propriétés des algorithmes 42 / 48
Preuve d’exactitude d’un algorithme itératif
Preuve d’exactitude d’un algorithme itératif
Exemple : l’assertion suivante est un invariant de la boucle for de la
fonction sum_all :
S(k)≡ res =k−1
∑j=0
liste[j],
où k est le nombre d’itérations effectuées
1 def sum_all(liste):2 res = 03 for item in liste:4 res += item5 return res
Programmation (Chap. 10) Prouver les propriétés des algorithmes 43 / 48
Preuve d’exactitude d’un algorithme itératif
Preuve d’exactitude d’un algorithme itératif
Preuve.
Base. Avant d’entrer dans la boucle, res = 0 qui est bien égal à
∑−1
j=0liste[j]. Donc S(0) est vraie.
Réc. Supposons que ce soit vrai pour S(k) (k ≥ 0), et prouvons le pour
S(k +1). Par induction, resold = ∑k−1
j=0liste[j]. Lors de la (k +1)ième
itération item = liste[k ], donc, par l’instruction 4,
resnew = resold + liste[k ] =k−1
∑j=0
liste[j]+ liste[k ] =k
∑j=0
liste[j].
Cela signifie qu’après l’exécution complète de la boucle, S(n) est vraie et
donc la valeur retournée estn−1
∑j=0
liste[j].
Programmation (Chap. 10) Prouver les propriétés des algorithmes 44 / 48
Preuve d’exactitude d’un algorithme itératif
Preuve d’exactitude d’un algorithme itératif
Tri par sélection
1 def selection_sort(t):2 n = len(t)3 for i in range(n-1):4 small = i5 for j in range(i+1,n):6 if t[j] < t[small]:7 small = j8 (t[i], t[small]) = (t[small], t[i])
Preuve de l’exactitude de selection_sort.
Au tableau.
Programmation (Chap. 10) Prouver les propriétés des algorithmes 45 / 48
Preuve d’exactitude d’un algorithme récursif
Preuve d’exactitude d’un
algorithme récursif
Programmation (Chap. 10) Prouver les propriétés des algorithmes 46 / 48
Preuve d’exactitude d’un algorithme récursif
Preuve d’exactitude d’un algorithme récursif
Prouver qu’un algorithme récursif est exact est naturel grâce, encore
une fois, à une preuve par récurrence.
def sum_rec(n):""" compute the sum of integers from 1 to n """if n == 1:
return 1else:
return sum_rec(n-1) + n
Preuve de l’exactitude de sum_rec.
Base. On suppose que n est un entier ≥ 1 (fini). Si n = 1, la condition
(3) est évaluée à vrai, et la valeur retournée est bien ∑1
i=1 i = 1.
Réc. Supposons que la fonction soit exacte pour n ≤ k (k ≥ 1), et
prouvons le pour n = k +1. Quand n = k +1 > 1, l’instruction 6
retourne, par hypothèse de récurrence
n−1
∑i=1
i +n =n
∑i=1
i.
Programmation (Chap. 10) Prouver les propriétés des algorithmes 47 / 48
Preuve d’exactitude d’un algorithme récursif
Preuve d’exactitude d’un algorithme récursif
Problème
Prouver que le calcul du nième nombre de Fibonacci est exact.
1 def fibo(n):2 if n == 0:3 return 04 elif n == 1:5 return 16 else:7 return fibo(n-1) + fibo(n-2)
Preuve.
Au tableau.
Programmation (Chap. 10) Prouver les propriétés des algorithmes 48 / 48
Chapitre 11. Complexité : évaluer l’efficacité desalgorithmes
Contenu du chapitre1 Efficacité des algorithmes
2 Quelques nouveaux algorithmes
3 Calculer le temps CPU des algorithmes
4 La notation grand-O et la notion de complexité dans le pire des cas
5 Evaluer la complexité des algorithmes
6 Complexité des opérations sur les dictionnaires
7 Comparaison de différents algorithmes
8 Comparaisons de la comlexité d’algorithmes sur des dictionnaires
9 Glossaire
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 1 / 126
Référence
Le contenu de ce chapitre n’est pas couvert dans le livre ThinkPythonde Downey.
En ce qui concerne la complexité, les références suivantes ont étéutilisées :
AHO, A. et ULLMAN, J., Concepts fondamentaux del’Informatique, Dunod (1993)
CORMEN, T. et LEISERSON, C., Introduction to algorithms, MITPress (1991)
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 2 / 126
Efficacité des algorithmes
Efficacité des algorithmes
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 3 / 126
Efficacité des algorithmes
Efficacité des algorithmes
Etant donné un algorithme :
comment évaluer son efficacité ?
comment comparer l’efficacité de deux algorithmes différentsrésolvant le même problème ?
Pour cela, on peut :
calculer le temps d’exécution utilisé par la machine pour effectuerun algorithme (on parle de temps CPU1) : temps utilisé par leprocesseur pour réaliser une tâche particulière;
évaluer l’efficacité théorique d’un algorithme, la croissance dutemps mis par un algorithme en fonction de la taille de sesentrées : on parle de la complexité de l’algorithme.
1Central Processing Unit.Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 4 / 126
Efficacité des algorithmes
Efficacité des algorithmes
L’efficacité d’un algorithme peut dépendre de la taille des entrées (parex. nombre d’éléments dans une liste, nombre d’équations dans unsystème à résoudre, etc.).
Pour une taille n donnée, l’efficacité d’un algorithme (par ex. un tri)peut également dépendre des valeurs données en entrée. On peutalors déterminer :
le meilleur des cas (par ex. les valeurs sont déjà triées);
le pire des cas (par ex. les valeurs sont triées par ordredécroissant).
Ainsi, on parlera de l’efficacité ou de la complexité d’un algorithme
dans le pire des cas;
dans le meilleur des cas;
en moyenne (entrées aléatoires).
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 5 / 126
Quelques nouveaux algorithmes
Quelques nouveauxalgorithmes
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 6 / 126
Quelques nouveaux algorithmes
Algorithme : tri par insertion
ProblèmeSoit une séquence de n entiers (n ≥ 0), comment trier (par ordrecroissant) les éléments de la séquence ?
Idée du tri par insertion.
Imaginons que nous devions trier une maind’un jeu de cartes. On pose les cartes nontriées sur la table (faces cachées). On prendune première carte en main : elle forme une“sous-main” triée. On prend une deuxièmecarte sur la table et on l’insére dans la mainde telle sorte que les deux cartes en mainsoient triées. On répète le processus jusqu’àavoir inséré toutes les cartes posées sur latable.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 7 / 126
Quelques nouveaux algorithmes
Algorithme : tri par insertion
Idée du tri par insertion.
Pour appliquer cette idée à une séquence d’entiers, on représente laséquence comme suit : la partie grisée est la partie triée de laséquence.
1
3 7 2 6 5 1 4
3 2 6 5 1 4
2 3 7 6 5 1 4
2 6 7 5 4
2 3 5 6 7 1 4
1 2 3 5 6 7 4
7
1 2 3 4 5 6 7
3
Deux boucles nécessaires :
une boucle pour trier chaque élément1 à 1 (sauf le premier) : indice i
une boucle, dans chaque itération dela première, pour déplacer leséléments plus grand que l’élément àtrier vers la droite : indice j
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 8 / 126
Quelques nouveaux algorithmes
Algorithme : tri par insertion
Idée du tri par insertion.
Pour appliquer cette idée à une séquence d’entiers, on représente laséquence comme suit : la partie grisée est la partie triée de laséquence.
1
3 7 2 6 5 1 4
3 2 6 5 1 4
2 3 7 6 5 1 4
2 6 7 5 4
2 3 5 6 7 1 4
1 2 3 5 6 7 4
7
1 2 3 4 5 6 7
3
Deux boucles nécessaires :
une boucle pour trier chaque élément1 à 1 (sauf le premier) : indice i
une boucle, dans chaque itération dela première, pour déplacer leséléments plus grand que l’élément àtrier vers la droite : indice j
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 8 / 126
Quelques nouveaux algorithmes
Algorithme : tri par insertion
Exemple. Quand l’indice de la première boucle vaut i , les i premierséléments sont triés (indices 0 à i−1). Appelons clef la valeur del’élément à trier. L’indice de la clef est i . Soit k la valeur de l’indice dela nouvelle position de la clef, après insertion : tous les éléments de lapartie triée dont les valeurs sont plus grandes que la clef ont un indicecompris entre k et i−1. On va utiliser une deuxième boucle d’indice jpour décaler ces éléments vers la droite.
... 3 2 6 5 1 47
1 3 4 5 620
ik
1 3 5 6 7 42
1 3 4 5 620
ik
...
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 9 / 126
Quelques nouveaux algorithmes
Algorithme : tri par insertion
Fonction en Python :1 def insertion_sort(t):2 n = len(t)3 for i in range(1,n):4 clef = t[i]5 j = i - 16 while j >= 0 and t[j] > clef:7 t[j+1] = t[j]8 j = j - 19 t[j+1] = clef
>>> t = [3, 7, 2, 6, 5, 1, 4]>>> insertion_sort(t)>>> t[1, 2, 3, 4, 5, 6, 7]
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 10 / 126
Quelques nouveaux algorithmes
Algorithme : tri par fusion
ProblèmeSoit une séquence de n entiers (n ≥ 0), comment trier (par ordrecroissant) les éléments de la séquence ?
Idée du tri par fusion.
Imaginons que nous devions trier une maind’un jeu de cartes et que Bob soit en notrecompagnie. On divise le paquet de cartes en2 parties (à peu près) égales. On donne lapremière partie à Bob, qui s’occupe de latrier. On lui donne alors la seconde partiepour qu’il la trie également. Ensuite, à partirdes deux paquets de cartes triées, onreconstitue un seul paquet de cartes trié enfusionnant les deux paquets triés.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 11 / 126
Quelques nouveaux algorithmes
Algorithme : tri par fusionIdée du tri par fusion.
Appliquons cette idée à une séquence d’entiers : on divise (split) et onfusionne deux parties triées (merge). Une séquence à 1 élément esttriée. Les séquences triées sont grisées.
split
3 7 2 6 5 1 4
3 7 2 6 5 1 4
split split
3 7 2 6 5 1 4
split split split
3 7 2 6 5 1
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 12 / 126
Quelques nouveaux algorithmes
Algorithme : tri par fusionIdée du tri par fusion.
Appliquons cette idée à une séquence d’entiers : on divise (split) et onfusionne deux parties triées (merge). Une séquence à 1 élément esttriée. Les séquences triées sont grisées.
merge
3 7 2 6 5 1 4
3 7 2 6 5 1 4
3 7 2 6 1 5 4
3 7 2 6 5 1
merge merge
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 12 / 126
Quelques nouveaux algorithmes
Algorithme : tri par fusionIdée du tri par fusion.
Appliquons cette idée à une séquence d’entiers : on divise (split) et onfusionne deux parties triées (merge). Une séquence à 1 élément esttriée. Les séquences triées sont grisées.
7
3 7 2 6 5 1 4
3 2 6 5 1 4
3 7 2 6 1 5 4
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 12 / 126
Quelques nouveaux algorithmes
Algorithme : tri par fusionIdée du tri par fusion.
Appliquons cette idée à une séquence d’entiers : on divise (split) et onfusionne deux parties triées (merge). Une séquence à 1 élément esttriée. Les séquences triées sont grisées.
Merge
3 7 2 6 5 1 4
2 3 6 7 1 4 5
3 7 2 6 1 5 4Merge
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 12 / 126
Quelques nouveaux algorithmes
Algorithme : tri par fusionIdée du tri par fusion.
Appliquons cette idée à une séquence d’entiers : on divise (split) et onfusionne deux parties triées (merge). Une séquence à 1 élément esttriée. Les séquences triées sont grisées.
3 7 2 6 5 1 4
2 3 6 7 1 4 5
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 12 / 126
Quelques nouveaux algorithmes
Algorithme : tri par fusionIdée du tri par fusion.
Appliquons cette idée à une séquence d’entiers : on divise (split) et onfusionne deux parties triées (merge). Une séquence à 1 élément esttriée. Les séquences triées sont grisées.
2 3 6 7 1 4 5Merge
1 2 3 4 5 6 7
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 12 / 126
Quelques nouveaux algorithmes
Algorithme : tri par fusionIdée du tri par fusion.
Appliquons cette idée à une séquence d’entiers : on divise (split) et onfusionne deux parties triées (merge). Une séquence à 1 élément esttriée. Les séquences triées sont grisées.
1 5 63 42 7
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 12 / 126
Quelques nouveaux algorithmes
Algorithme : tri par fusionIdée du tri par fusion.
On va écrire
une fonction split pour séparer une liste (approx.) en 2;
une fonction merge qui fusionne deux listes triées;une fonction récursive merge_sort
� Base : une liste vide ou à un seul élément est une liste triée;� Réc. : on divise la liste en 2, puis on trie récursivement les 2
parties, puis on fusionne les deux parties triées.
contrairement aux tris par sélection et insertion qui ne modifientque les valeurs à l’intérieur de la liste2, ici nous devons travailleravec des nouvelles sous-listes. Pour éviter les problèmes (cf.Chap. 7), les fonctions retourneront les listes.insertion_sort(t) # la liste est modifiee par la fonctiont = merge_sort(t) # on retourne la liste triee
2On parle de fonctions réalisant le travail “in place”.Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 14 / 126
Quelques nouveaux algorithmes
Algorithme : tri par fusion
Idée de l’algorithme merge.
On va écrire une fonction récursive merge qui fusionne deuxséquences A1 et A2 triées :
Base : si A1 est vide, alors A2 est la liste fusionnée; si A2 estvide, alors A1 est la liste fusionnée3
Réc. : si A1[0] < A2[0], alors A1[0] doit se trouver en premierdans la liste fusionnée. La liste fusionnée estA1[0]+merge(A�1,A2) où A�1 est A1 pour laquelle on a retiré lepremier élément. Le cas A1[0]≥ A2[0] est géré de manièresymétrique.
3Implique : si les deux listes sont vides, alors la liste fusionnée est vide.Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 15 / 126
Quelques nouveaux algorithmes
Algorithme : tri par fusion
Fonction split en Python :1 def split(t):2 """ precondition: len(t) >= 2 """3 mid = len(t) / 24 t1 = t[:mid]5 t2 = t[mid:]6 return (t1, t2)
>>> split([1,2])([1], [2])>>> split([1,2,3])([1], [2, 3])>>> split(range(10))([0, 1, 2, 3, 4], [5, 6, 7, 8, 9])
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 16 / 126
Quelques nouveaux algorithmes
Algorithme : tri par fusion
Fonction merge en Python :1 def merge(t1, t2):2 if len(t1) == 0:3 return t24 elif len(t2) == 0:5 return t16 elif t1[0] < t2[0]:7 return [t1[0]] + merge(t1[1:], t2)8 else:9 return [t2[0]] + merge(t1, t2[1:])
>>> merge([],[])[]>>> merge([],[1])[1]>>> merge([1, 2, 7, 8], [3, 4, 9])[1, 2, 3, 4, 7, 8, 9]
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 17 / 126
Quelques nouveaux algorithmes
Algorithme : tri par fusion
Fonction merge_sort en Python :1 def merge_sort(t):2 n = len(t)3 if n > 1:4 (t1, t2) = split(t)5 t1 = merge_sort(t1)6 t2 = merge_sort(t2)7 return merge(t1, t2)8 else:9 return t
>>> merge_sort(t)[1, 2, 3, 4, 5, 6, 7]>>> t[7, 4, 2, 1, 6, 5, 3]>>> t = merge_sort(t)>>> t[1, 2, 3, 4, 5, 6, 7]
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 18 / 126
Quelques nouveaux algorithmes
Efficacité des algorithmes de tri
Questions :
comment exprimer l’efficacité du tri par sélection (cf. Chap. 8), dutri par insertion ou du tri par fusion en fonction du nombre nd’éléments dans la séquence à trier ?
y-a-t il des cas (entrées) qui sont pires ou meilleurs que d’autres ?
quel est, des trois algorithmes de tri présentés, le plus efficace ?
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 19 / 126
Quelques nouveaux algorithmes
Efficacité des algorithmes du calcul de l’exposant
Les deux fonctions suivantes calculent an (cf. Chap. 8) :
1 def expo(a, n):2 r = 13 for i in range(n):4 r = a * r5 return r
1 def expo(a, n):2 r = 13 while n > 0:4 if n % 2 == 0:5 a = a * a6 n = n / 27 else:8 r = r * a9 n = n - 1
10 return r
comment exprimer formellement l’intuition selon laquelle ladeuxième version est plus efficace que la première ?
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 20 / 126
Quelques nouveaux algorithmes
Efficacité des algorithmes du calcul de nième nombre deFibonacci
Les deux fonctions suivantes calculent le nième nombre de Fibonacci :1 def fib_rec(n):2 if n == 0:3 return 04 elif n == 1:5 return 16 else:7 return fib_rec(n-1) + fib_rec(n-2)
1 def fib_iter(n):2 f = [0, 1]3 for i in range(2,n+1):4 f.append(f[i-1] + f[i-2])5 return f[n]
pourquoi la version récursive prend beaucoup plus de temps dèsque n > 30 ? (quand elle n’est pas interrompue par undépassement du nombre maximum d’appels récursifs !)
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 21 / 126
Calculer le temps CPU des algorithmes
Calculer le temps CPU desalgorithmes
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 22 / 126
Calculer le temps CPU des algorithmes
Evaluer le temps CPU d’un algorithme
On ne calcule pas le temps d’exécution réel d’un algorithme (par ex.avec une montre ou en récupérant dans le script l’heure avant et aprèsl’exécution d’un morceau de code) ! En effet,
le processeur n’est pas utilisé uniquement pour votre programme :même si vous quittez toutes les autres applications avant votre test, il ya quantité de programmes qui tournent en tâches de fond (qui vérifientsi un email est arrivé, si la connexion internet est ouverte, si un CD estdans le lecteur, mise à jour anti-virus, etc.)
il y a aussi d’autres problèmes techniques difficiles à estimer : y-a-t il uncache pour les fonctions, comment cela marche-t-il ?
On va plutôt évaluer le temps CPU : temps mis par le processeur pourexécuter uniquement un (morceau) de code donné (ce temps restenéanmoins dépendant du processeur, du système d’exploitation, etc. : on nepeut comparer que des tests réalisés sur une même machine).
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 23 / 126
Calculer le temps CPU des algorithmes
Evaluer le temps CPU d’un algorithme
Si on veut estimer le temps CPU d’un morceau de code Python, il estrecommandé d’utiliser le module timeit.
ce module règle automatiquement la plupart des problèmesévoqués ci-avant;néanmoins, une mesure du temps CPU avec timeit reste uneestimation, et le même morceau de code sur les mêmes entréespeut donner des temps différents. Dans ce cas :
� choisiriez-vous de calculer la moyenne des temps CPU pour avoirune bonne estimation ?
� Non ! S’il y a des différences de temps, elles ne sont pas dues àvotre test (le code et les entrées n’ont pas été modifiés) : il fautprendre le temps minimum.
� En pratique, on va effectuer un grand nombre de fois un morceaude code (une série de n tests), calculer d’autres séries de n tests :on gardera alors le temps de la série la plus rapide.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 24 / 126
Calculer le temps CPU des algorithmes
Evaluer le temps CPU d’un algorithme
Si on veut estimer le temps CPU d’un morceau de code Python, il estrecommandé d’utiliser le module timeit.
ce module règle automatiquement la plupart des problèmesévoqués ci-avant;néanmoins, une mesure du temps CPU avec timeit reste uneestimation, et le même morceau de code sur les mêmes entréespeut donner des temps différents. Dans ce cas :
� choisiriez-vous de calculer la moyenne des temps CPU pour avoirune bonne estimation ?
� Non ! S’il y a des différences de temps, elles ne sont pas dues àvotre test (le code et les entrées n’ont pas été modifiés) : il fautprendre le temps minimum.
� En pratique, on va effectuer un grand nombre de fois un morceaude code (une série de n tests), calculer d’autres séries de n tests :on gardera alors le temps de la série la plus rapide.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 24 / 126
Calculer le temps CPU des algorithmes
Utilisation de timeit en ligne de commande
Une façon d’utiliser timeit est de le faire en ligne de commande.
Syntaxe. Pour tester une fonction f qui se trouve dans un module m :
python -m timeit "import m; m.f(...)"
Exemple. La commande suivante va chercher la version itérative deFibonacci dans le fichier fibo.py :
python -m timeit "import fibo; fibo.fib_iter(30)"
produit
100000 loops, best of 3: 16 usec per loop
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 25 / 126
Calculer le temps CPU des algorithmes
Utilisation de timeit en ligne de commande
$ python -m timeit "import fibo; fibo.fib_iter(30)"100000 loops, best of 3: 16 usec per loop
le symbole $ est ajouté pour montrer qu’il s’agit d’une commande.
timeit a calculé 100000 fois fib_iter(30), et ce, 3 fois de suite.
le meilleur des 3 temps était � 1600000 µs, ce qui correspond à16 µs en moyenne pour chaque appel (loop) à fib_iter.
une milliseconde (ms, msec) correspond à un millième deseconde.
une microseconde (µs, usec) correspond à un millième demilliseconde.
au total, calculer 100000 fois fib_iter(30) a donc pris (dans lemeilleur des cas) 1.6 sec de temps CPU.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 26 / 126
Calculer le temps CPU des algorithmes
Utilisation de timeit en ligne de commande
$ python -m timeit "import fibo; fibo.fib_rec(30)"10 loops, best of 3: 943 msec per loop
le nombre de tests de la série a été réduit de 100000 à 10 partimeit
timeit a calculé 10 fois fib_rec(30), et ce, 3 fois de suite.
le meilleur des 3 temps était � 9430 ms, ce qui correspond à 943ms en moyenne pour chaque appel (loop) à fib_rec.
au total, calculer 10 fois fib_rec(30) a donc pris (dans lemeilleur des cas) 9.43 sec de temps CPU.
un appel à fib_iter(30) � 16 µs, un appel à fib_rec(30)� 943000 µs.
fib_iter(30) semble donc être fois � 60000 plus rapide quefib_rec(30) !
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 27 / 126
Calculer le temps CPU des algorithmes
Utilisation de timeit en ligne de commande
par défaut, le nombre de “loops” est calculé en essayant despuissances de 10 successives jusqu’à obtenir un temps total d’aumoins 0.2 sec.on peut spécifier le nombre de loops avec l’option -n$ python -m timeit -n 5 "import fibo; fibo.fib_rec(30)"5 loops, best of 3: 940 msec per loop
on peut spécifier le nombre de séries (3 par défaut) avec -r$ python -m timeit -n 5 -r 5 "import fibo; fibo.fib_rec(30)"5 loops, best of 5: 944 msec per loop
l’aide complète de l’utilisation en ligne de commande est obtenueavec l’option -h
$ python -m timeit -h
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 28 / 126
Calculer le temps CPU des algorithmes
Utilisation de timeit dans un script
Le module timeit peut également être utilisé dans un script ou enmode interactif (mais il faut préciser où il peut importer les fonctions àtester, même si le temps est calculé dans le script lui-même).>>> import timeit>>> t = timeit.Timer(’fibo.fib_iter(30)’,’import fibo’)>>> t.timeit()14.887954950332642
on initialise un objet Timer avec les commandes à tester (le premierargument est le code à tester, le second est le code initial, à exécuterune seule fois avant les tests)
la méthode timeit(n) effectue n fois (par défaut un million) le code àtester et retourne un temps (float) exprimé en secondes.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 29 / 126
Calculer le temps CPU des algorithmes
Utilisation de timeit dans un script
>>> t.repeat(3, 100000)[1.5052459239959717, 1.4943749904632568, 1.4944181442260742]>>> min(t.repeat())15.087707996368408
la méthode repeat(r,n) exécute r séries de n tests et retournent lesrésultats (exprimés en secondes) sous la forme d’une liste. Par défaut,r vaut 3 et n vaut un million.
la fonction min retourne le plus petit élément d’une séquence.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 30 / 126
Calculer le temps CPU des algorithmes
Utilisation de timeit dans un script
Nous créons un module cpu (disponible sur e-learning) :import timeit
def cpuTime(func_name, mod_name, args, n = 100000, r = 3):""" lance r series de n tests
sur la fonction func_name (dans le module mod_name)et avec args comme arguments
func_name, mod_name et args sont des strn et r sont des entiers (defaut: 100000 and 3)
retourne le temps moyen (en sec)pour une execution de la fonction dans la meilleure serie
"""stmt = mod_name + ’.’ + func_name + ’(’ + args + ’)’setup = ’import ’ + mod_namet = timeit.Timer(stmt,setup)res = min(t.repeat(r,n))return res / n
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 31 / 126
Calculer le temps CPU des algorithmes
Comparaison d’algorithmes
Dans le module fibo :import cpu
if __name__ == ’__main__’:print "Temps affiches en msec"print ’n: iter: rec:’for i in range (5,31, 5):
print ’%4d’ % i,print ’%.3f’ % (cpu.cpuTime(’fib_iter’, ’fibo’, str(i), 1000) * 1000),print ’%.3f’ % (cpu.cpuTime(’fib_rec’, ’fibo’, str(i), 10) * 1000)
for i in range (100, 1901, 200):print ’%4d’ % i,print ’%.3f ----’ % (cpu.cpuTime(’fib_iter’, ’fibo’, str(i), 1000)
* 1000)
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 32 / 126
Calculer le temps CPU des algorithmes
Comparaison d’algorithmes
Comparaison des deux fonctions pour Fibonacci :
Temps affiches en msecn: iter: rec:
5 0.003 0.00510 0.006 0.05815 0.008 0.65620 0.011 7.30525 0.013 82.96430 0.015 905.731
100 0.050 ----300 0.148 ----500 0.283 ----700 0.421 ----900 0.568 ----1100 0.737 ----1300 0.910 ----1500 1.123 ----1700 1.319 ----1900 1.514 ----
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 33 / 126
Calculer le temps CPU des algorithmes
Comparaison d’algorithmes
Comparaison des deux fonctions pour le calcul de l’exposant :
Temps affiches en msec. Calcul de 2^nn: class: amel:1000 0.394 0.0142000 0.950 0.0293000 1.757 0.0464000 2.699 0.0615000 3.702 0.0856000 4.826 0.0717000 6.119 0.1108000 7.561 0.1359000 9.275 0.16310000 10.891 0.140
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 34 / 126
Calculer le temps CPU des algorithmes
Comparaison d’algorithmes
Dans le module sort :import randomimport cpu
def test(n):t1 = range(n)t2 = range(n,0,-1)t3 = []for i in range(n):
t3.append(random.randint(0,n))print ’%6d ’ % n,print ’%7.2f’ % (cpu.cpuTime(’selection_sort’, ’sort’, str(t1), 100) * 1000),print ’%7.2f’ % (cpu.cpuTime(’insertion_sort’, ’sort’, str(t1), 100) * 1000),print ’%7.2f’ % (cpu.cpuTime(’merge_sort’, ’sort’, str(t1), 100) * 1000),print ’ %7.2f’ % (cpu.cpuTime(’selection_sort’, ’sort’, str(t2), 100) * 1000),print ’%7.2f’ % (cpu.cpuTime(’insertion_sort’, ’sort’, str(t2), 100) * 1000),print ’%7.2f’ % (cpu.cpuTime(’merge_sort’, ’sort’, str(t2), 100) * 1000),print ’ %7.2f’ % (cpu.cpuTime(’selection_sort’, ’sort’, str(t3), 100) * 1000),print ’%7.2f’ % (cpu.cpuTime(’insertion_sort’, ’sort’, str(t3), 100) * 1000),print ’%7.2f’ % (cpu.cpuTime(’merge_sort’, ’sort’, str(t3), 100) * 1000)
if __name__ == ’__main__’:print "Temps affiches en msec"print ’n t1: sel ins mer t2: sel ins mer t3: sel ins mer’for i in range(100, 1001, 100):
test(i)
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 35 / 126
Calculer le temps CPU des algorithmes
Comparaison d’algorithmes
Comparaison des trois fonctions de tri (t1 déjà trié, t2 trié par ordredécroissant, t3 aléatoire) en fonction de la taille n de la séquence :
Temps affiches en msecn t1: sel ins mer t2: sel ins mer t3: sel ins mer
100 0.95 0.05 0.83 0.96 1.76 0.92 0.93 1.03 1.27200 3.41 0.10 1.94 3.45 6.92 2.12 3.34 3.53 3.03300 7.56 0.15 3.24 7.73 15.47 3.45 7.51 7.66 5.12400 13.37 0.19 4.66 13.60 27.40 4.98 13.24 13.75 7.48500 20.79 0.24 6.43 21.23 44.01 6.63 20.49 22.13 10.14600 29.94 0.29 8.04 30.83 61.06 8.57 29.76 33.27 13.08700 40.68 0.34 9.79 42.10 86.26 10.52 40.88 43.80 16.03800 53.88 0.39 12.01 55.58 114.96 12.60 53.08 60.03 19.33900 67.70 0.44 14.14 70.14 145.48 14.76 68.23 76.18 23.071000 83.49 0.49 17.24 86.22 180.61 17.67 84.39 95.27 28.64
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 36 / 126
Calculer le temps CPU des algorithmes
Comparaison d’algorithmes
Interprétation des résultats :
liste croissante : le tri par insertion est nettement plus rapide queles deux autres (à votre avis, pourquoi ?); le tri par fusion estmeilleur que le tri par sélection (l’écart augmente quand naugmente).
liste décroissante : le tri par insertion est nettement plus lent queles deux autres; le tri par fusion est meilleur que les deux autres(même rmq sur l’écart).
liste aléatoire : les tris par insertion et sélection ont des temps dumême ordre de grandeur, le tri par fusion est meilleur que lesdeux autres (même rmq sur l’écart).
Cet “écart” est intéressant : ce qui va nous intéresser est la croissancedu temps d’exécution en fonction de n, plutôt que des chiffres. C’estce qu’exprime la complexité.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 37 / 126
La notation grand-O et la notion de complexité dans le pire des cas
La notation grand-O et lanotion de complexité dans le
pire des cas
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 38 / 126
La notation grand-O et la notion de complexité dans le pire des cas
Temps d’exécution
Pour analyser les temps d’exécution, nous allons définir une fonctionT (n) qui va représenter le nombre d’unités de temps pris par unalgorithme sur une entrée de taille n, et ce, dans le pire des cas.
Pour simplifier les calculs, on va considérer que les instructionsélémentaires prennent 1 unité de temps : assignation, opérationarithmétique sur des nombres, etc.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 39 / 126
La notation grand-O et la notion de complexité dans le pire des cas
Temps d’exécutionExemple : soit la fonction suivante, comment exprimer T (n) où n est lenombre d’éléments de la liste donnée en entrée.
1 def sum_all(liste):2 res = 03 for item in liste:4 res += item5 return res
T (n) = 2n +2 car
l’instruction 2 est exécutée 1 fois
l’instruction 3 est exécutée n fois (assignation de chaque élément)
l’instruction 4 est exécutée n fois
l’instruction 5 est exécutée 1 fois
C’est une approximation (on a pas tenu compte de l’appel à la fonction, dutemps exact de chaque instruction élémentaire, du nombre exactd’instructions élémentaires cachées dans l’instr. 3, etc.) mais cela a peud’importance : ce qui va nous intéresser est la croissance de T (n), pas lesconstantes.Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 40 / 126
La notation grand-O et la notion de complexité dans le pire des cas
Temps d’exécution
Ce qui nous intéresse c’est la croissance de T (n), pas les constantes.
Imaginons que chaque instruction élémentaire soit réalisée en 1 µs (1million d’instructions à la seconde) et que deux algorithmes A et B(pour le même problème) aient des temps TA(n) = 1000n etTB(n) = 2n2.
Temps d’exécution (en sec.) :
n A B10 0.01 0.0002
100 0.1 0.021000 1.0 2.0
10000 10.0 200.0100000 100.0 20000.0
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 41 / 126
La notation grand-O et la notion de complexité dans le pire des cas
Temps d’exécution
Dès que n > 500, A est bien meilleur que B : la constante 1000importe beaucoup moins que le fait que dans un cas on a une fonctionlinéaire, dans l’autre, elle est quadratique.Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 42 / 126
La notation grand-O et la notion de complexité dans le pire des cas
Temps d’exécutionComparaison de 1000x +1000, 10x2 et x3
100 − x sur [0,70]
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 43 / 126
La notation grand-O et la notion de complexité dans le pire des cas
Temps d’exécutionComparaison de 1000x +1000, 10x2 et x3
100 − x sur [600,2000]
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 44 / 126
La notation grand-O et la notion de complexité dans le pire des cas
Temps d’exécution
Les fonctions de temps suivantes sont classées par croissance. Lestermes fréquemment utilisés pour décrire certains temps sont misentre parenthèses.
T (n) = logn (temps logarithmique, la base importe peu)
T (n) =√
n
T (n) = n (temps linéaire)
T (n) = n logn
T (n) = n2 (temps quadratique)
T (n) = n3 (temps cubique)
T (n) = 2n (temps exponentiel, la base importe peu)
T (n) = n!
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 45 / 126
La notation grand-O et la notion de complexité dans le pire des cas
Temps d’exécutionComparaison de logn,
√n et n sur [0,50].
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 46 / 126
La notation grand-O et la notion de complexité dans le pire des cas
Temps d’exécutionComparaison de n, n logn et n2 sur [0,25].
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 47 / 126
La notation grand-O et la notion de complexité dans le pire des cas
Temps d’exécutionComparaison de n3 et 2n sur [0,15].
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 48 / 126
La notation grand-O et la notion de complexité dans le pire des cas
Temps d’exécutionComparaison de 2n et n! sur [1,20].
>>> for i in range(1,21):print ’%7d’ % 2**i,print ’%19d’ % fact(i)
2 14 28 6
16 2432 12064 720128 5040256 40320512 362880
1024 36288002048 399168004096 4790016008192 622702080016384 8717829120032768 130767436800065536 20922789888000
131072 355687428096000262144 6402373705728000524288 1216451004088320001048576 2432902008176640000
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 49 / 126
La notation grand-O et la notion de complexité dans le pire des cas
Taille maximale du problème, pour un temps donné
Imaginons qu’une machine exécute 1 million d’instructions à laseconde et que nous sommes prêts à attendre 1 heure de temps CPUpour résoudre un problème. Quelle est la taille maximale du problèmeque l’algorithme A (TA(n) = 1000n) peut résoudre ?
1000n ≤ 60×60×106 = 36×108,
donc n ≤ 36×105. L’algorithme A peut résoudre un problème de taille3600000 en 1 heure.
Pour l’algorithme B (TB(n) = 2n2) :
2n2 ≤ 36×108,
donc n ≤√
18×108 � 42426.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 50 / 126
La notation grand-O et la notion de complexité dans le pire des cas
Taille maximale du problème, pour un temps donné
Supposons qu’un algorithme dépense un temps f (n) pour résoudre unproblème de taille n et que la machine exécute 1 million d’instructionsà la seconde.
Taille maximale pour résoudre le problème en un temps donné :
f (n) 1 sec. 1 min. 1 heure 1 jourlnn 3×10434294 8×1026057668 � ∞ � ∞√
n 1012 3.6×1015 1.3×1019 7.5×1021
n 106 6×107 3.6×109 8.64×1010
n lnn 87847 3.9×106 1.8×108 3.9×109
n2 1000 7745 60000 293938
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 51 / 126
La notation grand-O et la notion de complexité dans le pire des cas
Taille maximale du problème, pour un temps donné
Supposons qu’un algorithme dépense un temps f (n) pour résoudre unproblème de taille n et que la machine exécute 1 million d’instructionsà la seconde.
Taille maximale pour résoudre le problème en un temps donné :
f (n) 1 sec. 1 min. 1 heure 1 jour 1 mois 1 an 1 sièclen3 100 391 1532 4420 13886 31595 1466522n 13 16 31 36 41 44 51n! 9 11 12 13 15 16 17
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 52 / 126
La notation grand-O et la notion de complexité dans le pire des cas
Notation grand-O
Soit T (n) un temps d’exécution mesuré en fonction de la taille n duproblème. Nous pouvons supposer que T (n) est une fonction telleque :
n est un entier positif ou nul;
T (n)≥ 0 ∀n.
On va décrire le taux de croissance de ce type de fonction en utilisantune notation spécifique, la notation O (prononcer “grand-O”). On parleaussi de la complexité (sous-entendu “dans le pire des cas”) de lafonction. Ceci permettra de classer les fonctions – et donc les tempsd’exécution des algorithmes – selon leur type de croissance,indépendamment des constantes et autres détails techniques.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 53 / 126
La notation grand-O et la notion de complexité dans le pire des cas
Notation grand-O
Les fonctions de complexité sont des fonctions
N→ R∗ = {r ∈ R | r ≥ 0} .
Intuition : Soit deux fonctions de complexité f (n) et g(n), on note
f (n) ∈ O (g(n)) ,
ouf (n) = O (g(n)) ,
si f ne croît pas plus vite que g.
On dit alors que “f est en grand-O de g”.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 54 / 126
La notation grand-O et la notion de complexité dans le pire des cas
Notation grand-O
“f est en grand-O de g”
Intuition : f ne croît pas plus vite que g.
Définition formelle :
f (n) ∈ O (g(n)) ⇐⇒ ∃n0 ∈ N,c ∈ R t.q. ∀n ≥ n0 : f (n)≤ c g(n),
où n0 ≥ 0 et c > 0.
Preuves : prouver qu’une fonction f (n) ∈ O (g(n)) reviendra(principalement) à déterminer n0 et c.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 55 / 126
La notation grand-O et la notion de complexité dans le pire des cas
Notation grand-O
f (n) ∈ O (g(n)) ⇐⇒ ∃n0 ∈ N,c ∈ R t.q. ∀n ≥ n0 : f (n)≤ c g(n)
0 n
c g(n)
f(n)
n
Vision asymptotique :
n0 ≥ 0 permet d’éviter les effetsde bord : ce n’est pas parcequ’un algorithme est plus rapidesur des petites valeurs, qu’il lesera encore asymptotiquement;
c > 0 exprime le fait que lesconstantes ne sont pas (très)importantes
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 56 / 126
La notation grand-O et la notion de complexité dans le pire des cas
Notation grand-O
f (n) ∈ O (g(n)) ⇐⇒ ∃n0 ∈ N,c ∈ R t.q. ∀n ≥ n0 : f (n)≤ c g(n)
Exemples :
Soit f (n) = 1000n et g(n) = n, alors f (n) ∈ O(g(n)). En effet, sin0 = 0 et c = 1000, alors
f (n) = 1000n ≤ 1000n = 1000g(n), ∀n ≥ 0.
Soit f (n) = 2n2 et g(n) = n2, alors f (n) ∈ O(g(n)). En effet, sin0 = 0 et c = 2, alors f (n)≤ 2g(n), ∀n ≥ 0.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 57 / 126
La notation grand-O et la notion de complexité dans le pire des cas
Notation grand-O
f (n) ∈ O (g(n)) ⇐⇒ ∃n0 ∈ N,c ∈ R t.q. ∀n ≥ n0 : f (n)≤ c g(n)
Exemples :
Soit f (n) = (n +1)2 et g(n) = n2, alors f (n) ∈ O(g(n)). En effet,si n0 = 1 et c = 4, alors
f (n) = (n+1)2 = n2 +2n+1≤ n2 +2n2 +n2 = 4n2 = 4g(n), ∀n≥ 1.
Notez que l’inégalité n’est pas vraie si n = 0, d’où n0 = 1.
Remarque : on aurait pu prendre d’autres valeurs (comme n0 = 3et c = 2)
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 58 / 126
La notation grand-O et la notion de complexité dans le pire des cas
Notation grand-O
Exemples :
n2 ∈ O(n3). Soit n0 = 0 et c = 1 : n2 ≤ n3,∀n≥ 0 est vrai (trivial).
n3 /∈ O(n2). Par l’absurde, supposons que n3 ∈ O(n2), alors ilexiste deux constantes n0 et c telles que n3 ≤ c n2,∀n ≥ n0.Cela signifie que
c ≥ n3
n2 = n, ∀n ≥ n0,
ce qui est une contradiction avec le fait que c est une constante.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 59 / 126
La notation grand-O et la notion de complexité dans le pire des cas
Simplification des expressions grand-OLa notation grand-O permet de simplifier les fonctions de complexité.
Lemme (les facteurs constants ne sont pas importants)Pour toute valeur constante d > 0 et toute fonction de complexité f (n),on a
f (n) ∈ O (d · f (n)) et d · f (n) ∈ O (f (n))
Preuve.Soit c = 1
d et n0 = 0, on a
f (n) = (c ·d)f (n) = c(d · f (n)), ∀n ≥ 0,
et donc f (n) ∈ O(df (n)). Pour le deuxième cas, il suffit de choisirc = d .
Exemples : 1000n2 et n2
1000 ∈ O(n2).
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 60 / 126
La notation grand-O et la notion de complexité dans le pire des cas
Simplification des expressions grand-O
Lemme (les termes d’ordre inférieurs sont négligeables)Soit une fonction de complexité f (n) polynomiale et dont le coefficientdu terme de plus grand degré est positif, c-à-d,
f (n) := ak nk +ak−1nk−1 + . . .+a2n2 +a1n +a0,
où ak > 0. Alors,f (n) ∈ O
�nk�
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 61 / 126
La notation grand-O et la notion de complexité dans le pire des cas
Simplification des expressions grand-O
Preuve.Soit n0 = 1 et c = ∑k
i=0, max(ai ,0) (somme des coefficients positifs).Donc c est bien une constante strictement positive car ak > 0. Alors,
f (n)≤ cnk , ∀n ≥ 1.
En effet, pour chaque coefficient aj :
si aj ≤ 0, on a certainement ajnj ≤ 0, ∀n ≥ 1.
si aj > 0, on a ajnj ≤ ajnk , ∀n ≥ 1 car j ≤ k .
Exemple : Soit f (n) := 3n5 +10n4−100n3 +n +1. Alorsf (n) ∈ O(n5). En effet, si c = 3+10+1+1 = 15, alors f (n)≤ 15n5,∀n ≥ 1.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 62 / 126
La notation grand-O et la notion de complexité dans le pire des cas
Simplification des expressions grand-OLe deuxième lemme exprime simplement que f (n) croît moins vite quenk , c-à-d
limn→∞
f (n)
nk = 0.
On peut généraliser cela : si g(n) croît moins vite que h(n), alors
g(n)+h(n) ∈ O(h(n)).
(on peut négliger ce qui croît moins vite)
Exemple : toute exponentielle croît plus vite que tout polynôme :
limn→∞
np
an = 0.
Donc, par exemple,2n +n3 ∈ O(2n).
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 63 / 126
La notation grand-O et la notion de complexité dans le pire des cas
Simplification des expressions grand-O
Lemme (Transitivité des expressions grand-O)Grand-O est une relation transitive, c-à-d que si f ∈ O(g) et g ∈ O(h),alors f ∈ O(h).
Preuve.Par définition,
f (n)≤ c1g(n), ∀n ≥ n1,
g(n)≤ c2h(n), ∀n ≥ n2.
Soit n0 = max(n1,n2) et c0 = c1 · c2, alors,
f (n)≤ c1g(n)≤ c1c2h(n) = c0h(n), ∀n ≥ n0.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 64 / 126
La notation grand-O et la notion de complexité dans le pire des cas
Simplification des expressions grand-O
Pour exprimer la complexité d’une fonction T (n) qui représente letemps d’exécution d’un algorithme, nous suivrons les deux règlessuivantes :
Simplification. On exprime T (n) en appliquant les règles desimplification des expressions grand-O. Ainsi, si T (n) = 2n2 +3,on dira que l’algorithme est exécuté en un temps O(n2) (outemps quadratique).
Proximité. Il est clair que si T (n) = 2n2 +3, alors T (n) ∈ O(n3)ou même O(2n), mais cela n’exprime pas la croissance de T (n).On dira donc que T (n) ∈ O(n2) (on choisit une fonction g dont lacroissance est la plus faible possible et pour laquelle f ∈ O(g).)
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 65 / 126
Evaluer la complexité des algorithmes
Evaluer la complexité desalgorithmes
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 66 / 126
Evaluer la complexité des algorithmes
Evaluer la complexité des algorithmes
Nous allons appliquer la notation grand-O pour évaluer la complexitédes algorithmes, en suivant quelques règles qui permettent desimplifier l’analyse et qui découlent directement des simplifications desexpressions grand-O.
Au lieu d’évaluer un temps d’exécution T (n) précisément, lessimplifications des expressions grand-O vont être appliquéesdirectement, en fonction des structures utilisées dans le programme àanalyser (boucles, instructions conditionnelles, etc.)
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 67 / 126
Evaluer la complexité des algorithmes
Evaluer la complexité des algorithmesUne instruction simple est une instruction qui réalise une action en untemps constant, quelle que soit la taille des entrées. Par exemple, lesinstructions suivantes sont des instructions simples : affectation,opérations arithmétiques sur des entiers ou des réels, opérateur [.]d’accès à un élément d’une liste ou d’une chaîne, instruction return,etc.
A l’inverse, toute instruction qui peut provoquer un temps d’exécutionnon constant n’est pas considérée comme une instruction simple :appel d’une fonction ou d’une méthode (dont appels récursifs), boucles,instructions conditionnelles, certains opérateurs appliqués sur desobjets de taille variable (comme l’opérateur “slice” sur les listes, soncoût en temps peut ne pas être constant, dépendant de la taille de latranche), etc.
Règle (Instruction simple)Par définition, une instruction simple à un coût en temps O(1) (tempsconstant).
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 68 / 126
Evaluer la complexité des algorithmes
Evaluer la complexité des algorithmes
On “additionne” la complexité de morceaux de code en ne gardant quela complexité la plus grande.
Règle (Sommation)Soit deux parties A et B d’un programme, exécutées de façonséquentiellea. Supposons TA(n) = O(fA(n)) et TB(n) = O(fB(n)).Alors
TA(n)+TB(n) = O(fB(n)+ fA(n)) = O(max(fB(n), fA(n)).
al’une après l’autre, la partie B n’est pas inclue dans le travail de la partie A etvice-versa.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 69 / 126
Evaluer la complexité des algorithmes
Evaluer la complexité des algorithmes
Exemple
def swap(a, b):temp = bb = aa = tempreturn (a, b)
Le temps de cet algorithme est en O(1) car sa complexité estO(1)+O(1)+O(1)+O(1).
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 70 / 126
Evaluer la complexité des algorithmes
Evaluer la complexité des algorithmes
Règle (Appel de fonction ou méthode)Le coût d’un appel à une fonction ou une méthode est équivalent aucoût de l’exécution du corps de la fonction.
Remarque : on expliquera par après comment évaluer le coût d’unefonction récursive, mais la règle ci-dessus reste d’application.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 71 / 126
Evaluer la complexité des algorithmes
Evaluer la complexité des algorithmes
Exemples
(a, b) = swap(a, b)(a, b) = swap(a, b)
Le temps de cet algorithme (totalement inutile) est O(1).
def f(n):res = g(n)res += h(n)return res
Imaginons que le temps d’exécution de g(n) soit en O(n) et celui deh(n) soit en O(n2), alors le temps d’exécution de f(n) est enO(n)+O(n2)+O(1) = O(n2).
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 72 / 126
Evaluer la complexité des algorithmes
Evaluer la complexité des algorithmes
Complexité des opérations sur les listes (d’après le site wiki python4) :
obtenir la longueur de la liste (n = len(t)) : O(1)
accéder à un élément d’un indice donné (x = t[i]) : O(1)
ajouter un élément en fin de liste (t.append(3)) : O(1)
insérer un élément (pire des cas: au début de la liste)(t.insert(0,8)) : O(n)
supprimer un élément (pire des cas: au début de la liste)(t.remove(4)) : O(n)
insérer un élément (meilleur des cas: en fin de liste)(t.insert(len(t),8)) : O(1)
supprimer un élément (meilleur des cas: en fin de liste) : O(1)
4http://wiki.python.org/moin/TimeComplexityProgrammation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 73 / 126
Evaluer la complexité des algorithmes
Evaluer la complexité des algorithmes
Complexité des opérations sur les listes – suite :
obtenir une tranche de taille k : O(k)
copier une liste : O(n) (par ex., la méthode extend copie la secondeliste à la fin de la première, donc O(k) si la seconde liste à k éléments)
concaténer : O(n1 +n2)
multiplier (= copier) une liste k fois : O(kn)
rechercher un élément dans la séquence (x in s) : O(n)
trouver le min. ou le max. d’une séquence (x = min(t)) : O(n)
l’appel à range est linéaire en le nombre d’éléments dans la listeretournée.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 74 / 126
Evaluer la complexité des algorithmes
Evaluer la complexité des algorithmes
Exemples
t.append(3)print 3 in tt2 = t * 2
Ce morceau de code a un temps linéaire :O(1)+O(n)+O(2n) = O(n)
t2 = t * len[t]
Cette instruction a un temps quadratique : O(n ·n) = O(n2)
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 75 / 126
Evaluer la complexité des algorithmes
Evaluer la complexité des algorithmesRègle (Boucles for)Le temps d’exécution d’une boucle for est équivalent à
complexité de la création de la séquence +# d’itérations (dans le pire des cas)× complexité du corps de la boucle
Remarques :
la notation grand-O est utilisée dans ce chapitre pour calculer lestemps d’exécution dans le pire des cas. Or, on peut sortir d’uneboucle de différentes façons (break, return, condition d’arrêt),d’où le pire des cas sur le # d’itérations.
n ·O(1) = O(n) et pas O(1) : la règle de sommation ne peut pass’appliquer car n n’est pas constant.
pour multiplier une expression grand-O par quelque chose quidépend de la taille du problème : f (n) ·O(g(n)) = O(f (n) ·g(n)).
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 76 / 126
Evaluer la complexité des algorithmes
Evaluer la complexité des algorithmes
Exemple1 def sum_all(liste):2 res = 03 for item in liste:4 res += item5 return res
Le temps de cet algorithme est O(n), où n est le nombre d’élémentsde la liste.
Preuve.La liste est déjà créée (car passée en paramètre), il n’y a donc pas decoût lié à son utilisation. Les instructions 2 et 5 sont en O(1). Laboucle est exécutée n fois. Le temps d’exécution du corps de laboucle (instr. 4) est en O(1). Donc le temps de cet algorithme estO(1)+n ·O(1) = O(n).
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 77 / 126
Evaluer la complexité des algorithmes
Evaluer la complexité des algorithmes
Exemple1 def sum_1_to_n(n):2 res = 03 for i in range(1, n + 1):4 res += i5 return res
Le temps de cet algorithme est O(n), où n est le nombre d’élémentsde la liste.
Preuve.La création de la séquence (fonction range) est en O(n). Lesinstructions 2 et 5 sont en O(1). La boucle est exécutée n fois. Letemps d’exécution du corps de la boucle (instr. 4) est en O(1). Doncle temps de cet algorithme est O(1)+O(n)+n ·O(1) = O(n).
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 78 / 126
Evaluer la complexité des algorithmes
Evaluer la complexité des algorithmes
Règle (Boucles while)Le temps d’exécution d’une boucle while est équivalent à
# d’itérations (dans le pire des cas) × (complexité de l’évaluation de lacondition + complexité du corps de la boucle)
Remarque :
à chaque itération, la condition est évaluée, donc il faut en tenircompte si celle-ci a un coût > O(1).
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 79 / 126
Evaluer la complexité des algorithmes
Evaluer la complexité des algorithmesExemple
1 def sum_1_to_n(n):2 t = range(1, n + 1)3 i = 04 res = 05 while i < n:6 res += t[i]7 i += 18 return res
Le temps de cet algorithme est O(n), où n est le nombre d’élémentsde la liste.
Preuve.Le temps de l’instr. 2 est en O(n). Les instructions 3, 4 et 8 sont enO(1). La boucle est exécutée n fois et son corps est en O(1). Letemps de l’évaluation de la condition est en O(1). Donc le temps decet algorithme est O(n)+n · (O(1)+O(1)) = O(n).
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 80 / 126
Evaluer la complexité des algorithmes
Evaluer la complexité des algorithmes
Exemple1 def is_include(t1, t2):2 i = 03 n1 = len(t1)4 while i < len(t1) and t1[i] in t2:5 i += 16 return i == n1
Cet algorithme retourne vrai ssi les éléments de t1 sont tous présentsdans t2. Le temps de cet algorithme est O(n1 ·n2), où n1 et n2 sont lestailles de t1 et t2.
Preuve.Le temps des instr. 2, 3 et 6 sont en O(1). La boucle est exécutée n1
fois et son corps est en O(1). Le temps de l’évaluation de la conditionest en O(n2). Donc le temps de cet algorithme estO(1)+n1 ·O(n2) = O(n1 ·n2).
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 81 / 126
Evaluer la complexité des algorithmes
Evaluer la complexité des algorithmes
Exercice1 def insertion_sort(t):2 n = len(t)3 for i in range(1,n):4 clef = t[i]5 j = i - 16 while j >= 0 and t[j] > clef:7 t[j+1] = t[j]8 j = j - 19 t[j+1] = clef
Quelle est la complexité dans le pire des cas du tri par insertion, enfonction du nombre n d’éléments à trier ?
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 82 / 126
Evaluer la complexité des algorithmes
Evaluer la complexité des algorithmes
Solution : le tri par insertion est en temps O(n2)
l’instruction 1 est en O(1)
considérons d’abord la boucle intérieure (while) : le corps decette boucle est en O(1) (instr. 7 et 8) et, dans le pire des cas, onsort de la boucle en ayant décalé tous les éléments, c-à-d quandle test j ≥ 0 n’est plus vrai. Comme j est initialisé à i−1, dans lepire des cas, on exécutera i fois la boucle (le nombre d’élémentsentre l’indice 0 et l’indice i−1 est i). Le temps de l’évaluation dela condition est en O(1). Sa complexité est donc O(i) (dans lepire des cas i = n−1 mais gardons cette expression en i pour lemoment car il représente l’indice de la boucle extérieure)
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 83 / 126
Evaluer la complexité des algorithmes
Evaluer la complexité des algorithmes
Solution : le tri par insertion est en temps O(n2)
considérons maintenant la boucle extérieure (for) : elle estexécutée exactement n−1 fois. L’appel à range est en O(n).Toutes les instructions du corps de la boucle sont en O(1), saufla boucle intérieure qui est en O(i). On peut calculer le tempstotal de la boucle comme suit grâce à la formule d’Euler :
O(n)+n−1
∑i=0
O(i) = O(0+1+. . .+n−1+n) = O
�n
∑i=1
i
�= O
�n2 +n
2
�
On en conclut que le temps du tri par insertion est en O(n2).
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 84 / 126
Evaluer la complexité des algorithmes
Evaluer la complexité des algorithmes
Et si la liste est déjà triée ?
alors l’analyse de la boucle intérieure montre qu’elle seraexécutée en temps constant (le test t[j] > clef étant faux pourchaque i).
l’analyse complète donne alors un temps en O(n) dans lemeilleur des cas.
cette analyse de la complexité explique les temps CPU observés.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 85 / 126
Evaluer la complexité des algorithmes
Evaluer la complexité des algorithmes
Dans le cas d’une instruction conditionnelle, on ne garde que le plusgrand temps d’exécution de toutes les branches.
Règle (Instructions conditionnelles)Soit une instruction conditionnelle avec k branches et � conditions àévaluer, et soit Tk(n) le temps d’exécution de la k ième branche. SiTi(n) ∈ O(fi(n)) pour i = 1, . . . ,k , et si l’évaluation de la condition jest en O(gj(n)), pour j = 1, . . . ,� alors
T (n) ∈ O
�max
i(fi(n))+max
j(gj(n))
�,
où T (n) est le temps d’exécution de l’instruction conditionnellecomplète.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 86 / 126
Evaluer la complexité des algorithmes
Evaluer la complexité des algorithmes
Exemple : attention aux opérations et appels de fonctions / méthodescachés dans les conditions
if x in t:print ’x est dans t’
else:print ’x pas dans t’
les deux branches sont en O(1)
le test du if est en O(n) (où n = len(t))
la complexité est donc O(n)+max(O(1),O(1)) = O(n)
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 87 / 126
Evaluer la complexité des algorithmes
Evaluer la complexité des algorithmes
Exemple : le temps du tri par sélection est en O(n2) où n est lenombre d’éléments de la liste à trier (et il n’y a pas de cas meilleur qued’autres).
1 def selection_sort(t):2 n = len(t)3 for i in range(n-1):4 small = i5 for j in range(i+1,n):6 if t[j] < t[small]:7 small = j8 (t[i], t[small]) = (t[small], t[i])
Preuve.Au tableau.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 88 / 126
Evaluer la complexité des algorithmes
Evaluer la complexité des algorithmes récursifs
Analyser le temps d’exécution d’une fonction récursive demande plusde travail :
on associe à chaque appel de f un temps T (n) inconnu qui définitle temps de f en fonction de n (taille ou valeur des arguments).on établit ensuite une définition récursive qui associe T (n) à unappel de f avec des arguments plus petits (ou à une autrefonction que f , en fonction du code à analyser) :
� Base. La taille des arguments est suffisamment réduite pourqu’aucun appel récursif ne soit fait par f .
� Réc. La taille des arguments est suffisamment grande pour quedes appels récursifs apparaissent. On suppose que tout appelrécursif ultérieur effectué par f sera effectué avec des argumentsplus petits (grâce à une preuve d’arrêt par ex.).
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 89 / 126
Evaluer la complexité des algorithmes
Evaluer la complexité des algorithmes récursifsLa relation de récurrence définissant T (n) s’établit grâce à l’examen du codede f , selon les actions suivantes :
pour chaque appel récursif à f , on utilise T (k) comme tempsd’exécution de l’appel, où k est la taille appropriée des arguments.
on évalue le temps du corps de f comme précédemment, mais engardant les termes T (k) sous la forme d’inconnues. On doit analyse fdeux fois : (1) en supposant que la taille n est suffisamment petite pourtomber dans un cas de base; (2) en supposant que n a une taille plusgrande. On obtient alors deux expressions pour le temps d’exécutionde f .
Dans les expressions obtenues, on remplace les termes grand-Ocomme O(h(n)) par c ·h(n) où c est une constante particulière.
Si a est une valeur de base pour la taille entrée, on initialise T (a) avecl’expression résultant de l’étape précédente. On initialise aussi T (n)avec l’expression obtenue pour le cas récursif.
Le temps d’exécution complet est déterminé par la résolution de cetterelation de récurrence.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 90 / 126
Evaluer la complexité des algorithmes
Evaluer la complexité des algorithmes récursifsExemple
1 def sum_rec(n):2 if n == 1:3 return 14 else:5 return sum_rec(n-1) + n
les appels récursifs sont réalisés avec des arguments plus petits(n−1).pour la base de la définition induction de T (n), on choisit n = 1.Dans ce cas, la complexité de T (1) est en O(1) (instr. 2 et 3).si n > 1, seules les instr. 2 et 4 et 5 sont exécutées. Lesinstructions 2 et 4 sont en O(1) et le temps d’exécution de l’instr.5 est T (n−1).on peut donc définir T (n) récursivement comme suit :
T (1) = O(1),T (n) = T (n−1)+O(1), ∀n > 1.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 91 / 126
Evaluer la complexité des algorithmes
Evaluer la complexité des algorithmes récursifs
on remplace les expressions O(h(n)) par c ·h(n) (une constantepour chaque expression) :
T (1) = a,T (n) = T (n−1)+b, ∀n > 1.
pour résoudre cette récurrence, on essaye d’exprimer T (n) enfonction de n et des constantes. Pour les premières valeurs on a :
T (1) = a,T (2) = T (1)+b = a+b,T (3) = T (2)+b = a+2b,T (4) = T (3)+b = a+3b.
à ce stade, on devine que T (n) = a+(n−1)b, ∀n ≥ 1.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 92 / 126
Evaluer la complexité des algorithmes
Evaluer la complexité des algorithmes récursifs
une preuve par récurrence simple permet de prouver que siT (n) = a+(n−1)b, ∀n ≥ 1, alors
T (1) = a,T (n) = T (n−1)+b, ∀n > 1.
on conclut que T (n) ∈ O(n) puisque a et b sont des constantes(> 0).
Intuition : la complexité d’un algorithme récursif est la complexitéd’une étape multipliée par le nombre d’étapes (la difficulté réside dansle calcul de ce nombre d’étapes).
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 93 / 126
Evaluer la complexité des algorithmes
Evaluer la complexité des algorithmes récursifs
Tri par fusion.Avant d’analyser le tri par fusion, et maintenant que l’on sait qu’ajouterou supprimer un élément en début de liste est en O(n) et en fin deliste en O(1), on remarque que les instructions des cas récursifs de lafonction merge peuvent être améliorées.
1 def merge(t1, t2):2 if len(t1) == 0:3 return t24 elif len(t2) == 0:5 return t16 elif t1[0] < t2[0]:7 return [t1[0]] + merge(t1[1:], t2)8 else:9 return [t2[0]] + merge(t1, t2[1:])
Dans l’instr. 7, le coût du “slice” est O(len(t1)−1) et la concaténationest en O(len(t1)+ len(t2)), ce qui donne du O(n) sans même compterl’appel récursif.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 94 / 126
Evaluer la complexité des algorithmes
Evaluer la complexité des algorithmes récursifs
On modifie donc le code pour travailler plutôt en fin des listes.1 def merge(t1, t2):2 if len(t1) == 0:3 return t24 elif len(t2) == 0:5 return t16 elif t1[-1] > t2[-1]:7 last = t1.pop()8 new = merge(t1, t2)9 new.append(last)
10 return new11 else:12 last = t2.pop()13 new = merge(t1, t2)14 new.append(last)15 return new
A part les appels récursifs, les autres instructions sont doncmaintenant en O(1).
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 95 / 126
Evaluer la complexité des algorithmes
Comparaison d’algorithmes
Cette modification améliore encore le temps CPU du tri par fusion :
Temps affiches en msecn t1: sel ins mer t2: sel ins mer t3: sel ins mer
100 0.96 0.05 0.71 0.97 1.79 0.64 0.94 0.95 0.89200 3.40 0.10 1.52 3.46 6.93 1.37 3.36 3.44 1.97300 7.64 0.15 2.37 8.02 15.46 2.13 7.52 7.43 3.15400 13.41 0.20 3.26 13.80 27.59 3.02 13.37 14.08 4.38500 21.77 0.30 4.55 21.85 42.71 3.95 20.82 22.43 5.72600 30.39 0.30 5.21 31.13 62.16 4.64 30.34 32.18 7.19700 41.54 0.35 6.06 42.85 87.28 5.46 41.03 45.52 8.48800 53.88 0.40 7.10 56.05 118.52 6.33 54.20 57.36 9.92900 68.56 0.45 7.88 71.28 142.15 7.40 68.54 71.84 11.371000 84.56 0.50 8.77 87.87 177.36 8.46 85.01 90.58 12.05
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 96 / 126
Evaluer la complexité des algorithmes
Evaluer la complexité des algorithmes récursifs
Tri par fusion.1 def merge_sort(t):2 n = len(t)3 if n > 1:4 (t1, t2) = split(t)5 t1 = merge_sort(t1)6 t2 = merge_sort(t2)7 return merge(t1, t2)8 else:9 return t
1011 def split(t):12 """ precondition: len(t) >= 2 """13 mid = len(t) / 214 t1 = t[:mid]15 t2 = t[mid:]16 return (t1, t2)
17 def merge(t1, t2):18 if len(t1) == 0:19 return t220 elif len(t2) == 0:21 return t122 elif t1[-1] > t2[-1]:23 last = t1.pop()24 new = merge(t1, t2)25 new.append(last)26 return new27 else:28 last = t2.pop()29 new = merge(t1, t2)30 new.append(last)31 return new
La complexité dans le pire des cas du tri par fusion (version améliorée)est en O(n log2 n), en fonction du nombre n d’éléments à trier.
Preuve.Au tableau.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 97 / 126
Complexité des opérations sur les dictionnaires
Complexité des opérations surles dictionnaires
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 98 / 126
Complexité des opérations sur les dictionnaires
Complexité des opérations sur les dictionnaires
Les dictionnaires ont une structure (sous-jacente) en Python qui leurpermet des performances très intéressantes pour accéder à unélément en moyenne (mais pas toujours dans le pire des cas). Ilsutilisent ce qu’on appelle une table de hachage.
En pratique, la complexité en moyenne sera souvent atteinte.
Moyenne Pire des casCopie O(n) O(n)Accès à un élément (via la clef) O(1) O(n)Mettre à jour un élément (via la clef) O(1) O(n)Supprimer un élément (via la clef) O(1) O(n)Obtenir une liste (de clefs ou de valeurs O(n) O(n)
Le nombre des paires dans le dictionnaire est n.
Source : http://wiki.python.org/moin/TimeComplexity
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 99 / 126
Complexité des opérations sur les dictionnaires
Complexité des opérations sur les dictionnaires
Pour tester cette complexité en moyenne, nous allons réaliser le testsuivant :
lire le fichier words.txt pour créer une liste t et un dictionnaire dcontenant les 113809 mots;utiliser le module cpu (cf. Chap. 9) pour évaluer le tempsd’exécution de la recherche d’un mot :
� avec une recherche linéaire dans la liste (complexités moyenne etpire des cas en O(n));
� avec une recherche dichotomique dans la liste, puisque les motssont triés (complexités en moyenne et pire des cas en O(log2 n));
� avec accès direct dans un dictionnaire (complexité en moyenneen O(1), dans le pire des cas en O(n)).
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 100 / 126
Complexité des opérations sur les dictionnaires
Complexité des opérations sur les dictionnaires
Création des données :file = open(’words.txt’)
t = []d = {}for line in file:
word = line.strip()t.append(word)d[word] = ’’
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 101 / 126
Complexité des opérations sur les dictionnaires
Complexité des opérations sur les dictionnaires
Recherche linéaire :def linear_search(t, w):
for word in t:if word == w:
return Truereturn False
Recherche dichotomique : voir Travaux Pratiques.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 102 / 126
Complexité des opérations sur les dictionnaires
Complexité des opérations sur les dictionnaires
Recherche dans le dictionnaire :def dico_search(d, w):
return w in d
Pour obtenir un mot aléatoire :import random
def random_word(t):index = random.randint(0,len(t)-1)return t[index]
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 103 / 126
Complexité des opérations sur les dictionnaires
Complexité des opérations sur les dictionnaires
Résultats obtenus en faisant appel au module cpu : on fait deux typesde tests (mots existants ou non).
Temps affiches en usec
Tests sur mots aleatoires (existants):linear dicho dict5205.32 14.29 4.40
Tests sur mots non presents:linear dicho dict11799.67 10.55 0.43
Remarque : dans les tests sur les mots aléatoires, le coût de lacréation d’un mot aléatoire est inclus dans les temps ci-dessus, mais ilest le même pour les 3 méthodes.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 104 / 126
Comparaison de différents algorithmes
Comparaison de différentsalgorithmes
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 105 / 126
Comparaison de différents algorithmes
Comparaison de différents algorithmesLe tri par fusion est donc bien plus rapide que les tris par sélection etinsertion.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 106 / 126
Comparaison de différents algorithmes
Comparaison de différents algorithmes
Calcul de an.
1 def expo(a, n):2 r = 13 for i in range(n):4 r = a * r5 return r
1 def expo(a, n):2 r = 13 while n > 0:4 if n % 2 == 0:5 a = a * a6 n = n / 27 else:8 r = r * a9 n = n - 1
10 return r
La complexité du calcul de an est en O(n) (version simple) et enO(log2 n) (version améliorée).
Preuve.Au tableau.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 107 / 126
Comparaison de différents algorithmes
Comparaison de différents algorithmes
Calcul de Fibonacci.1 def fib_rec(n):2 if n == 0:3 return 04 elif n == 1:5 return 16 else:7 return fib_rec(n-1) + fib_rec(n-2)
1 def fib_iter(n):2 f = [0, 1]3 for i in range(2,n+1):4 f.append(f[i-1] + f[i-2])5 return f[n]
La complexité du calcul de fn est en O(ϕn) (version récursive) et enO(n) (version itérative), où ϕ = 1+
√5
2 � 1.618 est le nombre d’or.
Idée de preuve.Au tableau (pas une preuve formelle : explique l’intuition).
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 108 / 126
Comparaison de différents algorithmes
Comparaison de différents algorithmesLa complexité du calcul récursif de Fibonacci est donc exponentielle !
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 109 / 126
Comparaisons de la comlexité d’algorithmes sur des dictionnaires
Comparaisons de la comlexitéd’algorithmes sur des
dictionnaires
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 110 / 126
Comparaisons de la comlexité d’algorithmes sur des dictionnaires
Trouver la (les) clef(s) d’une valeur donnée
def get_keys(d, value):t = []for k in d:
if d[k] == value:t.append(k)
return t
>>> d = histogram(’evenement’)>>> d{’m’: 1, ’n’: 2, ’e’: 4, ’t’: 1, ’v’: 1}>>> get_keys(d,4)[’e’]>>> get_keys(d,1)[’m’, ’t’, ’v’]
Complexité : pour rappel, append est en O(1). On a donc un temps enO(n) en moyenne et en O(n2) dans le pire des cas (dû à d[k]).
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 111 / 126
Comparaisons de la comlexité d’algorithmes sur des dictionnaires
Inverser les clefs et les valeurs d’un dictionnaire
def invert_dict(d):inv = {}for k in d:
val = d[k]if val not in inv:
inv[val] = [k]else:
inv[val].append(k)return inv
>>> d = histogram(’evenement’)>>> inv_d = invert_dict(d)>>> inv_d{1: [’m’, ’t’, ’v’], 2: [’n’], 4: [’e’]}
Complexité : O(n) en moyenne et O(n3) dans le pire des cas.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 112 / 126
Comparaisons de la comlexité d’algorithmes sur des dictionnaires
Retour sur Fibonacci amélioré
known = {0 : 0, 1 : 1}
def fib_dict(n):if n in known:
return known[n]res = fib_dict(n-1) + fib_dict(n-2)known[n] = resreturn res
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 113 / 126
Comparaisons de la comlexité d’algorithmes sur des dictionnaires
Comparaison des temps CPU des 3 méthodes
Temps affiches en msecn: iter: rec: dict:
5 0.003 0.014 0.00010 0.006 0.062 0.00015 0.008 0.665 0.00020 0.011 7.370 0.00025 0.013 80.641 0.00030 0.015 901.055 0.000100 0.050 ---- 0.000300 0.148 ---- 0.000500 0.297 ---- 0.000700 0.373 ---- 0.000900 0.496 ---- 0.000
1100 0.628 ---- 0.0001300 0.773 ---- 0.0001500 0.930 ---- 0.0001700 1.150 ---- 0.0001900 1.410 ---- 0.000
Comment expliquer cela ? Revenons d’abord sur la notion de variableglobale.Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 114 / 126
Comparaisons de la comlexité d’algorithmes sur des dictionnaires
Variables globales
Quand on définit une variable en dehors de toute fonction, elle estcréée une fois pour toute et est utilisable dans toutes les fonctions dumodule (ou dans d’autres modules grâce à la notation point, per ex.math.pi.
Une variable définie dans une fonction ou un paramètre d’une fonctionest une variable locale : sa portée est limitée à la fonction dans lequelelle est définie.
Par contre, la portée d’une variable définie en dehors de toute fonctionest globale, d’où le nom de variable globale.
Remarque : on peut toujours se passer de variables globales, et engénéral on essaye de les éviter car il peut y avoir des effets de bord,un manque de clarté (voir ci-après).
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 115 / 126
Comparaisons de la comlexité d’algorithmes sur des dictionnaires
Variables globales
Exemple : a est globale, b et c sont locales à la fonction f.>>> a = 3>>> def f(b):
c = 2print ’a:’, a, ’b:’, b, ’c:’, c
>>> f(4)a: 3 b: 4 c: 2>>> print a3>>> print bNameError: name ’b’ is not defined>>> print cNameError: name ’c’ is not defined
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 116 / 126
Comparaisons de la comlexité d’algorithmes sur des dictionnaires
Variables globalesQue se passe-t-il si on réassigne une variable dans une fonction etdont le nom est le nom d’une variable globale ?>>> a = 3>>> def g():
a = 7print a
>>> g()7>>> a3
la variable globale n’est pas modifiée
en réalité, on a recréé (assignation = création ou mise à jour) unevariable locale du même nom
on a déjà vu ce phénomène avec les paramètres (qui sont des variableslocales)
si on ne crée pas de variable locale : on utilise la variable globale,sinon, la variable locale du même nom “cache” la variable globale.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 117 / 126
Comparaisons de la comlexité d’algorithmes sur des dictionnaires
Variables globalesQue se passe-t-il si on applique une méthode sur une variable globalequi est mutable ?>>> x = [1, 2, 3]>>> def h():
x.append(4)
>>> h()>>> x[1, 2, 3, 4]>>> def h2():
x = []
>>> h2()>>> x[1, 2, 3, 4]
la valeur de la variable globale est modifiée dans h (on ne crée pas devariable locale puisqu’il n’y a pas d’assignation).
dans h2 par contre, on crée un variable locale.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 118 / 126
Comparaisons de la comlexité d’algorithmes sur des dictionnaires
Variables globales
Comprenez-vous cette erreur ? Comment la corriger ?>>> count = 0>>> def f():
count += 1
>>> f()UnboundLocalError: local variable ’count’ referenced before assignment
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 119 / 126
Comparaisons de la comlexité d’algorithmes sur des dictionnaires
Variables globales
Les fonctions globals et locals peuvent être utilisées pour connaîtreles variables globales ou locales disponibles à un endroit du code(elles retournent un dictionnaire).
Soit le fichier scope.py :def f(b):
a = 4c = 3print ’Dans f: var. globales: ’, globals(), ’\n’print ’Dans f: var. locales: ’, locals(), ’\n’
def g():if ’a’ in globals(): # clefs sous forme de str
print ’Dans g: a =’, a
a = 1f(2)g()
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 120 / 126
Comparaisons de la comlexité d’algorithmes sur des dictionnaires
Variables globales
La commande python scope.py produit :
Dans f: var. globales: {’a’: 1, ’g’: <function g at 0x77370>,’f’: <function f at 0x773b0>, ’__builtins__’: <module ’__builtin__’ (built-in)>,’__file__’: ’scope.py’, ’__package__’: None,’__name__’: ’__main__’, ’__doc__’: None}
Dans f: var. locales: {’a’: 4, ’c’: 3, ’b’: 2}
Dans g: a = 1
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 121 / 126
Comparaisons de la comlexité d’algorithmes sur des dictionnaires
Quelques algorithmes sur des dictionnairesAnalysons le comportement de la version “dictionnaire” de Fibonacci :known = {0 : 0, 1 : 1}
def fib_dict(n):if n in known:
return known[n]res = fib_dict(n-1) + fib_dict(n-2)known[n] = resreturn res
la première fois que l’on fait appel à cette fonction, toutes les valeurs dela séquence jusqu’à n sont stockées dans known.
cela signifie que si j’exécute la même fonction à nouveau avec unparamètre m ≤ n, le seul travail à réaliser est d’aller chercher la valeurdans le dictionnaire (O(1) en moyenne).
si j’exécute la même fonction à nouveau avec un paramètre m > n, leseul travail à réaliser est de calculer les m−n nouveaux nombres de laséquence (O(m−n) en moyenne).
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 122 / 126
Comparaisons de la comlexité d’algorithmes sur des dictionnaires
Quelques algorithmes sur des dictionnairesTemps affiches en msecn: iter: rec: dict:
5 0.003 0.014 0.00010 0.006 0.062 0.00015 0.008 0.665 0.00020 0.011 7.370 0.00025 0.013 80.641 0.00030 0.015 901.055 0.000100 0.050 ---- 0.000300 0.148 ---- 0.000500 0.297 ---- 0.000700 0.373 ---- 0.000900 0.496 ---- 0.000
1100 0.628 ---- 0.0001300 0.773 ---- 0.0001500 0.930 ---- 0.0001700 1.150 ---- 0.0001900 1.410 ---- 0.000
pour chaque valeur de n dans ces tests, 3 séries de 1000 appels sontréalisées pour calculer le temps moyen, dans la meilleure série;
cela explique que nous avons un temps inférieur à 0 µs !
il faut donc interpréter ces résultats avec prudence : la version avecdictionnaire “triche” en conservant en mémoire un “cache” (qui peut êtregrand et saturer la mémoire).
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 123 / 126
Comparaisons de la comlexité d’algorithmes sur des dictionnaires
Longs entiersSi vous calculez le 50ième nombre de Fibonacci, vous obtenez ceci :>>> fib_dict(50)12586269025L
Le L signifie qu’il s’agit d’un entier de type “long”.>>> type(10), type(10L)(<type ’int’>, <type ’long’>)>>> 1000 * 10001000000>>> 1000000 * 10000001000000000000L
un entier de type int a une taille limitée5.
dès qu’un nombre entier dépasse cette limite, Python le transforme enlong.
un entier de type long a une taille arbitrairement grande.
quand les entiers deviennent très grands, les opérationsmathématiques consomment plus d’espace et de temps.
5Plus grand int sur une machine “32bits” avec Python 2.6 :231−1 = 2147483647 (peut varier en fonction de la machine / version Python)Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 124 / 126
Glossaire
Glossaire
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 125 / 126
Glossaire
Glossaire
temps CPU : temps mis par le processeur pour exécuter un programme, le temps CPUvarie d’une machine à l’autre et est difficile à estimer précisément.
instruction simple : instruction qui est exécutée en temps constant.
notation grand-O : notation qui permet d’exprimer la croissance d’une fonction, demanière simplifiée.
complexité : la complexité d’un algorithme est la croissance, dans le pire des cas, de sontemps d’exécution. Elle est exprimée grâce à la notation grand-O en fonction de la tailledes entrées.
Programmation (Chap. 11) Complexité : évaluer l’efficacité des algorithmes 126 / 126
Chapitre 12. Fichiers et exceptions
Contenu du chapitre
1 Lecture et écriture de fichiers
2 Noms de fichiers et chemins
3 Sauvegarder des objets
4 Exceptions
5 Gérer les exceptions
6 Glossaire
Programmation (Chap. 12) Fichiers et exceptions 1 / 50
Lecture et écriture de fichiers
Lecture et écriture de fichiers
Programmation (Chap. 12) Fichiers et exceptions 2 / 50
Lecture et écriture de fichiers
Persistence
Les programmes réalisés jusqu’à présent s’exécutent pour un court
laps de temps et produisent des sorties à l’écran. Quand ils se
terminent, leurs données disparaissent.
D’autres programmes sont persistants : ils tournent longtemps (ou tout
le temps); gardent une partie de leurs données de manière
permanente (sur un disque dur par ex.); et s’ils sont interrompus et
relancés, ils reviennent là où ils en étaient.
Exemples : systèmes d’exploitation, serveurs web, programmes de
traitement de texte, etc.
Un moyen simple de conserver des données (objet de ce chapitre) :
lire et écrire dans des fichiers “textes” ou sauvegarder l’état du
programme dans une base de données simple (modules pickle et
shelve).
Programmation (Chap. 12) Fichiers et exceptions 3 / 50
Lecture et écriture de fichiers
Lecture dans un fichier texte
Un fichier texte est une séquence de caractères stockée sur un media
permanent comme un disque dur, un CD-ROM ou une clef USB.
Nous avons vu comment lire un fichier ligne par ligne grâce à la
méthode readline ou une boucle for.
Script readLines.py :
import sys
filename = sys.argv[1]
f = open(filename)
firstLine = f.readline()
print ’First line:’, firstLine.strip()
for i, line in enumerate(f):
print ’Line’, i, ’:’, line.strip()
Programmation (Chap. 12) Fichiers et exceptions 4 / 50
Lecture et écriture de fichiers
Lecture dans un fichier texte
La lecture est séquentielle, c-à-d que chaque ligne est lue l’une après
l’autre et que chaque appel à readline ou chaque itération de la
boucle for avance la position courante de la lecture d’une ligne dans
le fichier.
Fichier example.txt :
Un
Deux
Trois
Résultat de python readLines.py example.txt :
First line: Un
Line 0 : Deux
Line 1 : Trois
Programmation (Chap. 12) Fichiers et exceptions 5 / 50
Lecture et écriture de fichiers
Lecture dans un fichier texte
Alternativement, on peut lire l’entièreté d’un fichier avec la méthode
read.
Script readAll.py :
import sys
filename = sys.argv[1]
f = open(filename)
content = f.read()
print ’content:’, repr(content)
Remarque : la fonction repr prend n’importe quel objet en paramètre
et affiche une représentation de celui-ci sous forme de chaîne. Dans
le cas des chaînes de caractères, cela permet de voir les caractères
spéciaux (comme \n).
Programmation (Chap. 12) Fichiers et exceptions 6 / 50
Lecture et écriture de fichiers
Lecture dans un fichier texte
Résultat de python readAll.py example.txt :
content: ’Un\nDeux\nTrois’
Toutes les méthodes des chaînes (comme split) sont alors
disponibles pour travailler sur le contenu, mais si le fichier est très
grand, il vaut mieux travailler ligne par ligne.
Remarque : il existe également la méthode readlines (notez le “s”)
qui retourne une liste dont les éléments sont les lignes du fichier
(chaînes).
Programmation (Chap. 12) Fichiers et exceptions 7 / 50
Lecture et écriture de fichiers
Lecture dans un fichier texte
Pour réouvrir un fichier (par ex., pour changer le mode “lecture” en
“écriture” ou pour revenir au début du fichier), il faut d’abord le fermer
avec close, puis l’ouvrir à nouveau avec open.
>>> f = open(’example.txt’)
>>> content = f.read()
>>> content2 = f.read()
>>> content
’Un\nDeux\nTrois’
>>> content2
’’
>>> f.close()
>>> f = open(’example.txt’)
>>> content2 = f.read()
>>> content2
’Un\nDeux\nTrois’
Programmation (Chap. 12) Fichiers et exceptions 8 / 50
Lecture et écriture de fichiers
Ecriture dans un fichier texte
Pour ouvrir un fichier en mode “écriture”, on doit spécifier le mode :
soit w (write) qui crée un nouveau fichier et l’ouvre en écriture;
soit a (append) qui ouvre un fichier existant et permet l’écriture à
la fin de celui-ci.
Attention : si le fichier existe déjà et qu’on l’ouvre en mode w, l’ancien
fichier est écrasé et son contenu supprimé !
On utilise la méthode write pour écrire dans le fichier. Celle-ci
n’ajoute pas de caractère de fin de ligne. Au besoin, il faut les spécifier
avec \n.
Programmation (Chap. 12) Fichiers et exceptions 9 / 50
Lecture et écriture de fichiers
Ecriture dans un fichier texte
Script write.py :
import sys
filename = sys.argv[1]
f = open(filename, ’w’)
f.write(’Line 1\n’)
f.write(’Line 2\n’)
f.close()
f = open(filename)
content = f.read()
print content
f.close()
f = open(filename, ’a’)
f.write(’New text\n’)
f.close()
f = open(filename)
content = f.read()
print content
f.close()
Programmation (Chap. 12) Fichiers et exceptions 10 / 50
Lecture et écriture de fichiers
Ecriture dans un fichier texte
Résultat de python write.py test.txt :
Line 1
Line 2
Line 1
Line 2
New text
Le fichier test.txt est créé et contient les trois lignes de texte.
Programmation (Chap. 12) Fichiers et exceptions 11 / 50
Lecture et écriture de fichiers
Opérateur de format
L’argument de write doit être une chaîne de caractères. Il faut donc
convertir les autres types de valeurs avant de les écrire dans un fichier.
Un moyen simple de la faire est d’utiliser str. On peut également
utiliser l’opérateur de format %
>>> f = open(’test.txt’, ’w’)
>>> x = 52
>>> f.write(x)
TypeError: argument 1 must be string or read-only character buffer, not int
>>> f.write(str(x))
>>> a = 12
>>> euros = 32.56
>>> s = ’%d %.2f’ % (a, euros)
>>> s
’12 32.56’
>>> f.write(s)
>>> f.close()
Programmation (Chap. 12) Fichiers et exceptions 12 / 50
Lecture et écriture de fichiers
Opérateur de format
%d Entier (base 10)
%i Entier (base 10)
%o Entier (octal, base 8)
%x Entier (hexadecimal, base 16, en minuscules)
%X Entier (hexadecimal, base 16, en majuscules)
%e Réel, format scientifique (base 10, minuscule)
%E Réel, format scientifique (base 10, majuscule)
%f Réel (base 10)
%g Réel (base 10) : format scientifique ou décimal en fonction de
l’exposant, n’affiche pas les zéros non significatifs
%r Chaîne (convertit tout objet avec repr)
%s Chaîne (convertit tout objet avec str)
%% Pas d’argument converti, retourne le caractère %
D’autres formats existent :
voir docs.python.org/lib/typesseq-strings.html
Programmation (Chap. 12) Fichiers et exceptions 13 / 50
Lecture et écriture de fichiers
Opérateur de format
Exemples :
>>> t = (2,34,56)
>>> print ’Time is %dh%dmin%dsec’ % t
Time is 2h34min56sec
>>> a = 12
>>> print ’a is %d (decimal) or %o (octal) or %X (hexadec)’ % ((a,) * 3)
a is 12 (decimal) or 14 (octal) or C (hexadec)
>>> x = 10**9
>>> y = math.pi
>>> print ’%e %E %f %g’ % ((x,) *4)
1.000000e+09 1.000000E+09 1000000000.000000 1e+09
>>> t = range(4)
>>> print ’List: %r’ % t
List: [0, 1, 2, 3]
>>> x = 0.1
>>> print ’x: %r (i.e. %s)’ % (x, x)
x: 0.10000000000000001 (i.e. 0.1)
>>> print ’ratio = %g%%’ % (2.0 / 3)
ratio = 0.666667%
Programmation (Chap. 12) Fichiers et exceptions 14 / 50
Lecture et écriture de fichiers
Opérateur de format
Pour les entiers ou les chaînes, on peut spécifier le nombre (minimum)
de caractères utilisés pour réaliser un alignement (avec des espaces
ajoutés automatiquement avant ce qu’il faut afficher).
%xd (ou %xs) : x est un nombre qui signifie : utiliser x caractères
(minimum) pour afficher le nombre (la chaîne).
Pour les réels, on peut également spécifier la précision affichée.
%x.yf : x et y sont des nombres signifient : afficher y chiffres
après la virgules, et utiliser x caractères (minimum) pour afficher
le nombre.
Programmation (Chap. 12) Fichiers et exceptions 15 / 50
Lecture et écriture de fichiers
Opérateur de format
Exemple :
import math
def f(n):
return math.sqrt(math.sqrt(2**n))
print ’%3s %12s’ % (’n’,’f(n)’)
for n in range(10,111,10):
print ’%3d %12.2f’ % (n, f(n))
n f(n)
10 5.66
20 32.00
30 181.02
40 1024.00
50 5792.62
60 32768.00
70 185363.80
80 1048576.00
90 5931641.60
100 33554432.00
110 189812531.25
Programmation (Chap. 12) Fichiers et exceptions 16 / 50
Lecture et écriture de fichiers
Opérateur de format
On peut également utiliser des “flags”.
0 (zéro) Le nombre est affiché avec des zéros le précédant
’ ’ (espace) Un espace est ajouté avant un nombre positif
+ Un caractère de signe (+ ou −) est ajouté avant le nombre
- La valeur est ajustée à gauche
import random
def f():
return random.randint(-20, 20)
print ’Time is %02d:%02d:%02d’ % (6, 4, 17)
for i in range(3):
print ’% 3d’ % f()
print ’***’
for i in range(3):
print ’%+3d’ % f()
print ’***’
for i in range(3):
print ’%- 3d*’ % f()
Time is 06:04:17
14
7
-9
***
-13
+20
-3
***
-16*
-2 *
9 *
Programmation (Chap. 12) Fichiers et exceptions 17 / 50
Noms de fichiers et chemins
Noms de fichiers et chemins
Programmation (Chap. 12) Fichiers et exceptions 18 / 50
Noms de fichiers et chemins
Noms de fichiers et chemins
Les fichiers sont organisés en répertoires (ou dossiers). Chaque
programme possède son “répertoire courant” (par défaut, le répertoire
dans lequel on lance la commande python sur un script, ou un
répertoire fixé dans la configuration pour IDLE).
Le module os (os = operating system) possède une fonction getcwd
(cwd = current working directory) qui retourne le nom du répertoire
courant.
>>> import os
>>> print os.getcwd()
/Users/hadmelot/Documents
Programmation (Chap. 12) Fichiers et exceptions 19 / 50
Noms de fichiers et chemins
Noms de fichiers et chemins
Une chaîne comme /home/username/filename.txt qui identifie un
fichier est appelé un chemin.
un chemin absolu commence à la racine (répertoire le plus haut,
symbolisé par “/”) du système. Exemple :
/home/username/filename.txt1;
un chemin relatif commence depuis le répertoire courant (il ne
commence donc pas par /, il peut être symbolisé par “.”). On
peut remonter un répertoire avec les symboles “../”. Exemples :
../data/file.txt; ./file.txt.
1Syntaxe Linux ou Mac, sous windows, on remplace “/” par “\”. L’attribut os.sep
permet d’obtenir le séparateur du système d’exploitation sur lequel tourne le script.
Programmation (Chap. 12) Fichiers et exceptions 20 / 50
Noms de fichiers et chemins
Noms de fichiers et chemins
Dans une instruction comme open(’test.txt’) le chemin est donc
relatif au répertoire courant. Pour obtenir le chemin absolu d’un fichier,
on peut utiliser os.path.abspath (path est un sous-module du module
os).
Quelques autres fonctions des modules os et os.path :
os.path.exists vérifie qu’un fichier (ou un répertoire) existe;
os.path.isdir vérifie s’il s’agit d’un répertoire;
os.path.isfile vérifie s’il s’agit d’un fichier;
os.listdir retourne une liste de fichiers et de répertoires
contenus dans le répertoire passé en paramètre;
os.path.join prend les noms d’un répertoire et d’un fichier en
paramètre et les joint en un chemin complet (en utilisant le
séparateur spécifique au système d’exploitation).
Programmation (Chap. 12) Fichiers et exceptions 21 / 50
Noms de fichiers et chemins
Noms de fichiers et chemins
Exemples
>>> os.path.abspath(’memo.txt’)
’/Users/hadmelot/Documents/memo.txt’
>>> os.path.exists(’memo.txt’)
True
>>> os.path.isdir(’memo.txt’)
False
>>> os.path.isdir(’music’)
True
>>> os.listdir(os.getcwd())
[’memo.txt’, ’music’, ’photos’]
Programmation (Chap. 12) Fichiers et exceptions 22 / 50
Noms de fichiers et chemins
Noms de fichiers et chemins
Exemple
Le script suivant permet d’afficher les fichiers d’un répertoire donné en
argument de la ligne de commande, et affiche (récursivement) le
contenu des sous-répertoires.
import os
import sys
def walk(dir):
for name in os.listdir(dir):
path = os.path.join(dir, name)
if os.path.isfile(path):
print path
else:
walk(path)
walk(sys.argv[1])
Remarque : le module os possède une fonction walk qui fait presque
la même chose que cette fonction.
Programmation (Chap. 12) Fichiers et exceptions 23 / 50
Sauvegarder des objets
Sauvegarder des objets
Programmation (Chap. 12) Fichiers et exceptions 24 / 50
Sauvegarder des objets
Pickling
Dans ce qu’on a vu précédemment, il faut convertir les données en
chaînes de caractères pour les écrire dans un fichier. Ce n’est pas
toujours pratique pour sauver, puis récupérer, des objets plus
complexes que des chaînes ou des entiers (par ex. listes,
dictionnaires, etc.).
Le module pickle permet de sérialiser un objet, c-à-d, de le sauver,
puis de le récupérer en l’état, très facilement.
La fonction pickle.dumps prend un objet en paramètre et retourne un
représentation de l’objet sous forme de chaîne. La chaîne obtenue
n’est pas destinée à être compréhensible ou lue par l’humain. Par
contre, la fonction pickle.loads prend une chaîne de ce type en
paramètre et reconstitue un objet identique à celui donné à
pickle.dumps.
Programmation (Chap. 12) Fichiers et exceptions 25 / 50
Sauvegarder des objets
Pickling
>>> t = [1, 2, 3]
>>> pickle.dumps(t)
’(lp0\nI1\naI2\naI3\na.’
>>> pickle.dumps({’a’:3, ’b’:8, ’c’:9})
"(dp0\nS’a’\np1\nI3\nsS’c’\np2\nI9\nsS’b’\np3\nI8\ns."
>>> pickle.dumps(’hello’)
"S’hello’\np0\n."
>>> pickle.dumps(12)
’I12\n.’
>>> save_t = pickle.dumps(t)
>>> t2 = pickle.loads(save_t)
>>> t2
[1, 2, 3]
>>> t == t2
True
>>> t is t2
False
Programmation (Chap. 12) Fichiers et exceptions 26 / 50
Sauvegarder des objets
Pickling
Les fonctions dump et load (sans “s”) prennent un fichier comme
argument supplémentaire pour sauvegarder / récupérer un objet
depuis le fichier.
>>> f = open(’data.txt’, ’w’)
>>> d = {’a’:3, ’b’:8, ’c’:9}
>>> pickle.dump(d, f)
>>> f.close()
>>> # on peut fermer IDLE et relancer une session>>> # d est sauvegarde dans data.txt>>> f = open(’data.txt’)
>>> d = pickle.load(f)
>>> d
{’a’: 3, ’c’: 9, ’b’: 8}
Programmation (Chap. 12) Fichiers et exceptions 27 / 50
Sauvegarder des objets
Shelve
Le pickling est un moyen simple de sauvegarder un objet (ou plusieurs
objets séquentiellement dans le même fichier).
Un autre moyen, plus souple, est d’utiliser le module shelve qui fournit
une solution de persistance de type “base de données”.
Concrètement,
la base de donnée est écrite (automatiquement) dans un fichier;
la fonction shelve.open, crée un fichier de sauvegarde s’il
n’existe pas, et retourne un objet de type shelve;
un shelve fonctionne (presque) comme un dictionnaire. La
plupart des opérations et méthodes des dictionnaires lui sont
applicables;
toute modification appliquée au shelve est (automatiquement)
sauvegardée;
la fonction shelve.close permet de fermer le shelve, comme
pour un fichier.
Programmation (Chap. 12) Fichiers et exceptions 28 / 50
Sauvegarder des objets
Shelve
>>> import shelve
>>> t = range(4)
>>> p = (1, 2, 3)
>>> i = 12
>>> s = ’hello’
>>> db = shelve.open(’data.db’)
>>> db[’liste’] = t
>>> db[’point’] = p
>>> db[’entier’] = i
>>> db[’chaine’] = s
>>> print db
{’liste’: [0, 1, 2, 3], ’chaine’: ’hello’, ’entier’: 12, ’point’: (1, 2, 3)}
>>> db.close()
>>> # on peut fermer IDLE et relancer une session>>> # la base de donnee est sauvegardee a chaque modification>>> db = shelve.open(’data.db’)
>>> print db[’liste’]
[0, 1, 2, 3]
>>> db[’chaine’] = ’bonjour’
>>> print db
{’liste’: [0, 1, 2, 3], ’chaine’: ’bonjour’, ’entier’: 12, ’point’: (1, 2, 3)}
Programmation (Chap. 12) Fichiers et exceptions 29 / 50
Exceptions
Exceptions
Programmation (Chap. 12) Fichiers et exceptions 30 / 50
Exceptions
Exceptions
Les exceptions sont fréquentes, en particulier quand on manipule des
fichiers.
>>> 3 / 0
ZeroDivisionError: integer division or modulo by zero
>>> t = range(4)
>>> t[4]
IndexError: list index out of range
>>> s = ’2’ + 2
TypeError: cannot concatenate ’str’ and ’int’ objects
>>> open(’xyz.txt’)
IOError: [Errno 2] No such file or directory: ’xyz.txt’
>>> open(’/etc/passwd’, ’w’)
IOError: [Errno 13] Permission denied: ’/etc/passwd’
>>> open(’music’)
IOError: [Errno 21] Is a directory: ’music’
Programmation (Chap. 12) Fichiers et exceptions 31 / 50
Exceptions
Lancer une exception
Une exception indique que quelque chose d’exceptionnel s’est produit.
Vous pouvez-vous même lancer des exceptions.
def square_root(a, eps = 10**-9):
if not isinstance(a,int) and not isinstance(a,float):
raise TypeError(’a must be an integer or a float’)
if a < 0.0:
raise ValueError(’a must be >= 0’)
x = 0.0
approx = 1.0
while (abs(approx - x) > eps):
x = approx
approx = (x + (a / x)) / 2.0
return approx
>>> square_root(4)
2.0
>>> square_root(-2)
ValueError: a must be >= 0
>>> square_root(’hello’)
TypeError: a must be an integer or a float
Programmation (Chap. 12) Fichiers et exceptions 32 / 50
Exceptions
Lancer une exception
Syntaxe : raise ExceptionType(’message’)
Les types d’exceptions les plus fréquentes que vous pouvez lancer :
ValueError Erreur de valeur (par ex., ne respecte pas les pré-
conditions)
TypeError Erreur de type pour un paramètre
IndexError Indice hors bornes
IOError Erreur d’entrée / sortie (par ex. fichiers)
Voir aussi : docs.python.org/library/exceptions.html et
docs.python.org/tutorial/errors.html
Programmation (Chap. 12) Fichiers et exceptions 33 / 50
Gérer les exceptions
Gérer les exceptions
Programmation (Chap. 12) Fichiers et exceptions 34 / 50
Gérer les exceptions
Gérer les exceptions
Jusqu’à présent, quand une exception se produit, le script s’interrompt
brutalement. Comment gérer les exceptions pour éviter cette sortie
brutale et effectuer du code spécifique quand une exception se
produit ?
Solution 1 : utiliser des instructions conditionnelles pour éviter les
exceptions.
Exemple : avant d’ouvrir un fichier, vérifier qu’il existe, que ce n’est
pas un répertoire, etc. avec des fonctions comme os.path.exists ou
os.path.isdir.
Programmation (Chap. 12) Fichiers et exceptions 35 / 50
Gérer les exceptions
Gérer les exceptions
Solution 1 : utiliser des instructions conditionnelles pour éviter les
exceptions.
Inconvénients :
code difficile à lire et à écrire car nombreux cas possibles;
les tests peuvent être coûteux en temps de calcul;
nécessite de penser aux cas exceptionnels, et de les gérer “a
priori”, plutôt que de se concentrer sur le code principal;
le nombre d’exceptions possibles peut-être très important. Par
exemple, pour les fichiers, l’exception suivante suggère qu’il y a
au moins 21 types d’erreurs possibles !
>>> open(’music’)
IOError: [Errno 21] Is a directory: ’music’
comment être sûr de ne pas oublier un cas exceptionnel ?
Programmation (Chap. 12) Fichiers et exceptions 36 / 50
Gérer les exceptions
Gérer les exceptions
Il vaudrait donc mieux pouvoir écrire le code principal et demander à
l’interpréteteur d’essayer (try) de l’exécuter, et gérer les problèmes
seulement s’ils se produisent.
C’est exactement ce que propose la syntaxe try – except.
Solution 2 (de loin préférée) : utiliser une instruction try – except.
Programmation (Chap. 12) Fichiers et exceptions 37 / 50
Gérer les exceptions
Gérer les exceptions
La forme la plus simple d’une instruction try – except est la suivante.
Syntaxe :
try:
# code principal
except:
# code specifique en cas d’exception
Comportement :
Le code principal est exécuté. Dès que celui-ci produit une exception,
il est interrompu et le code spécifique est exécuté. Si aucune
exception ne s’est produite, le code spécifique n’est pas exécuté.
On dit qu’on rattrape (catch) une exception (dans une clause except).
Programmation (Chap. 12) Fichiers et exceptions 38 / 50
Gérer les exceptions
Gérer les exceptions
Exemple :
try:
print ’Avant’
raise ValueError(’Test’)
print ’Apres’
except:
print ’Gestion exception’
print ’Fin’
Résultat :
Avant
Gestion exception
Fin
Programmation (Chap. 12) Fichiers et exceptions 39 / 50
Gérer les exceptions
Gérer les exceptions
Exemple :
try:
i = int(raw_input(’Entier : ’))
print ’Nous pouvons travailler avec’, i
except:
print ’Je ne peux travailler qu\’avec un entier’
print ’Fin du programme’
Si on entre un entier :
Entier : 2
Nous pouvons travailler avec 2
Fin du programme
Sinon :
Entier : hello
Je ne peux travailler qu’avec un entier
Fin du programme
Programmation (Chap. 12) Fichiers et exceptions 40 / 50
Gérer les exceptions
Gérer les exceptions
Il est possible de spécifier dans la clause except le type d’exception
que l’on veut rattraper.
Exemple :
while True:
try:
x = int(raw_input("Please enter a number: "))
break
except ValueError:
print "Oops! That was no valid integer. Try again..."
print ’Now I\’m sure that’, x, ’is a valid integer.’
Remarque : dans cet exemple, si une autre exception qu’une
ValueError se produit, elle n’est pas rattrapée et le comportement
sera celui habituel : le programme s’arrête brutalement.
Programmation (Chap. 12) Fichiers et exceptions 41 / 50
Gérer les exceptions
Gérer les exceptions
On peut spécifier plusieurs clauses except, pour gérer différents types
d’exceptions.
Exemple :
try:
f = open(’myfile.txt’)
s = f.readline()
i = int(s.strip())
except IOError:
print ’Cannot open myfile.txt’
except ValueError:
print ’Could not convert’, s.strip(), ’to an integer.’
except:
print ’Unexpected error.’
raise
Remarque : le dernier cas permet de relancer les exceptions qui n’ont
pas été spécifiquement gérées (par ex. si ce code est encapsulé dans
une fonction, l’appelant pourra alors gérer ces exceptions).
Programmation (Chap. 12) Fichiers et exceptions 42 / 50
Gérer les exceptions
Gérer les exceptions
On peut spécifier un tuple d’exceptions à gérer dans une même clause
except.
Exemple :
try:
f = open(’myfile.txt’)
s = f.readline()
i = int(s.strip())
except (IOError, ValueError):
print ’Cannot open file or cannot convert integer’
Programmation (Chap. 12) Fichiers et exceptions 43 / 50
Gérer les exceptions
Gérer les exceptions
On peut placer un else, après les clauses except. Il sera exécuté si
aucune exception n’est produite. C’est utile dans le cas où il faut
exécuter du code seulement si l’instruction try n’a pas provoqué
d’exceptions.
Exemple :
import sys
for arg in sys.argv[1:]:
try:
f = open(arg, ’r’)
except IOError:
print ’cannot open’, arg
else:
print arg, ’has’, len(f.readlines()), ’lines’
f.close()
Remarque : l’utilisation d’une clause else est ici meilleure que d’ajouter du
code dans le try car cela évite d’attraper accidentellement une exception qui
serait provoquée par une autre instruction que open.
Programmation (Chap. 12) Fichiers et exceptions 44 / 50
Gérer les exceptions
Gérer les exceptions
Pour obtenir des informations sur une exception, on peut la faire suivre
par le mot-clef as et le nom d’une variable. Cette variable possède
comme valeur un objet de type exception. En affichant celle-ci, on
obtient le message associé à l’exception.
Exemple :
try:
f = open(’myfile.txt’)
except IOError as e:
print ’Cannot open myfile.txt:’, e
else:
s = f.readline().strip()
print s
Programmation (Chap. 12) Fichiers et exceptions 45 / 50
Gérer les exceptions
Gérer les exceptions
Enfin, on peut ajouter une clause finally qui sera exécutée dans tous
les cas (qu’il y ait une exception ou pas). Cela permet de réaliser des
tâches de “clean-up” comme fermer un fichier qu’on savait ouvert
avant le try, ou sauvegarder les données avant que le programme
s’arrête suite à une exception, etc. La clause finally est exécutée
juste avant de sortir du try – except, qu’une exception soit provoquée
ou non.
Exemple :
try:
raise KeyboardInterrupt
finally:
print ’Goodbye, world!’
Goodbye, world!
KeyboardInterrupt
Remarques : la commande “Ctrl-C” permet d’interrompre l’exécution d’un
programme. En Python, une telle commande provoque un
KeyboardInterrupt.
Programmation (Chap. 12) Fichiers et exceptions 46 / 50
Gérer les exceptions
Gérer les exceptions
Exemple :
import time
print ’This is a malicious script!’
print ’Try to find the exit before the bomb exploses!’
try:
for i in range(10):
print 10-i,
if i % 2 == 0:
print ’tic’
else:
print ’tac’
time.sleep(1)
except KeyboardInterrupt:
print ’Well done! You find the exit!’
else:
print ’BOOOM! You lose!’
finally:
print ’Goodbye!’
Programmation (Chap. 12) Fichiers et exceptions 47 / 50
Gérer les exceptions
Gérer les exceptions
Exercice : Quels sont les messages que la fonction suivante va
afficher (et dans quel ordre) si
x = 2 et y = 1;
x = 2 et y = 0;
x = ’2’ et y = ’1’ ?
def divide(x, y):
try:
result = x / y
except ZeroDivisionError:
print "division by zero!"
else:
print "result is", result
finally:
print "executing finally clause"
Programmation (Chap. 12) Fichiers et exceptions 48 / 50
Glossaire
Glossaire
Programmation (Chap. 12) Fichiers et exceptions 49 / 50
Glossaire
Glossaire
fichier texte : séquence de caractères stockée dans un support permanent (disque dur,
CD-ROM, etc.).
répertoire : (dossier) un répertoire possède son propre nom et contient une collection de
fichiers ou d’autres répertoires.
chemin : chaîne de caractères qui identifie un fichier.
chemin absolu : chemin qui commence au répertoire du plus haut niveau du système.
chemin relatif : chemin qui commence depuis le répertoire courant.
lancer (une exception) : on lance une exception quand quelque chose d’exceptionnel se
produit, via l’instruction raise.
rattraper (une exception) : on rattrape une exception dans une clause except, pour y
exécuter du code spécifique.
Programmation (Chap. 12) Fichiers et exceptions 50 / 50
Chapitre 13. Introduction aux objets
Contenu du chapitre
1 Classes et objets
2 Fonctions pures
3 Méthodes
4 Programmation orientée objets
5 Glossaire
Programmation (Chap. 13) Introduction aux objets 1 / 48
Classes et objets
Classes et objets
Programmation (Chap. 13) Introduction aux objets 2 / 48
Classes et objets
La notion de classe
Nous avons déjà rencontré de nombreux objets (en Python tout estobjet), comme des objets de types entier, chaîne ou liste.
Chaque objet possède donc un type. En fonction de celui-ci, une sériede méthodes, d’attributs et d’opérateurs sont disponibles.
Une classe est un modèle décrivant un type d’objets : ses attributs,ses méthodes, etc.
On distingue donc les deux notions :
objet : instance (valeur) d’un certain type;
classe : modèle décrivant un type et ses propriétés.
Programmation (Chap. 13) Introduction aux objets 3 / 48
Classes et objets
La notion de classe
On peut définir ses propres types en donnant une définition de classe.On peut donc voir une définition de classe comme la définition d’unnouveau type.
Définissons un nouveau type (classe) pour représenter des pointsdans le plan.class Point(object):
""" represente un point dans le plan """
le mot-clef class indique le début d’une définition de classe;
on donne ensuite le nom de la classe (par convention, avec unemajuscule);
entre parenthèses, on précise qu’un Point est une sorte d’object, qui est un type prédéfini.
Programmation (Chap. 13) Introduction aux objets 4 / 48
Classes et objets
La notion de classe
Pour l’instant notre modèle (classe) est très peu précis. On a justedonné un nom et un docstring.>>> print Point
<class ’__main__.Point’>
>>> help(Point)
Help on class Point in module __main__:
class Point(__builtin__.object)
| represente un point dans le plan
|
| Data descriptors defined here:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)
Programmation (Chap. 13) Introduction aux objets 5 / 48
Classes et objets
La notion d’objets
Cependant, on peut déjà créer des objets de la classe Point. Pour cefaire, on appelle Point comme si cela était une fonction.>>> p = Point()
>>> print p
<__main__.Point object at 0xe73bd0>
la valeur de retour est une référence vers un objet de type Point,que l’on assigne à la variable p;
créer un objet est une instanciation, et l’objet est une instance dela classe Point;
quand on affiche une instance, Python nous dit à quelle classeelle appartient et où elle est stockée en mémoire (sous la formed’une adresse représentée par un nombre hexadécimal).
Programmation (Chap. 13) Introduction aux objets 6 / 48
Classes et objets
La notion d’objets
>>> p = Point()
>>> print p
<__main__.Point object at 0xe73bd0>
Point
p 0xe73bd0
Programmation (Chap. 13) Introduction aux objets 7 / 48
Classes et objets
Attributs
Une des propriétés des objets sont ses attributs. Un attribut est unevaleur.
Par exemple, pour représenter un point dans le plan, on a besoin dedeux attributs (coordonnées x et y ).
Pour assigner un attribut à un objet, on peut utiliser la notation point.>>> p.x = 2.0
>>> p.y = 4.0
Point
p
x
y
2.0
4.0
Programmation (Chap. 13) Introduction aux objets 8 / 48
Classes et objets
Attributs
On accède aux attributs d’un objet également via la notation point.
p.x peut être lu comme “aller dans l’objet référé par p et récupérer lavaleur de son attribut x”>>> print p.x
2.0
>>> y = p.y
>>> print y
4.0
>>> print ’(%g, %g)’ % (p.x, p.y)
(2, 4)
>>> dist_from_orig = math.sqrt(p.x**2 + p.y**2)
>>> print dist_from_orig
4.472135955
Programmation (Chap. 13) Introduction aux objets 9 / 48
Classes et objets
Attributs
On peut passer un objet en argument d’une fonction. Le paramètre estassigné avec la référence de l’objet, il s’agit donc d’un alias et lesattributs de l’objet peuvent être modifiés (les objets sont mutables1).def print_point(pt):
print ’(%g, %g)’ % (pt.x, pt.y)
def double_point(pt):
pt.x *= 2
pt.y *= 2
>>> print_point(p)
(2, 4)
>>> double_point(p)
>>> print_point(p)
(4, 8)
Point
x
y
2.0
4.0pt
p
1Par défaut, les objets créés par le programmeur sont mutables (ce qu’on peutempêcher : ne sera pas vu à ce cours).Programmation (Chap. 13) Introduction aux objets 10 / 48
Classes et objets
Attributs
Un attribut peut être une référence vers un autre objet.class Rectangle(object):
""" represente un rectangle dans le planattributs: width, height, corner
(coin inferieur gauche, Point)"""
>>> box = Rectangle()
>>> box.width = 100.0
>>> box.height = 200.0
>>> box.corner = Point()
>>> box.corner.x = 0.0
>>> box.corner.y = 0.0
x
y
0.0
0.0
Pointwidth
height
corner
200.0
100.0
Rectangle
Programmation (Chap. 13) Introduction aux objets 11 / 48
Classes et objets
Objets comme valeurs de retour
Une fonction peut créer un objet et retourner la référence vers celui-ci.def find_center(r):
c = Point()
c.x = box.corner.x + box.width / 2.0
c.y = box.corner.y + box.height / 2.0
return c
>>> center = find_center(box)
>>> print_point(center)
(50, 100)
Programmation (Chap. 13) Introduction aux objets 12 / 48
Classes et objets
Copie d’objetsUn objet passé en argument peut-être modifié par une fonction. Celapeut provoquer des problèmes dans certains cas (cfr. Chap. 7).
On peut copier un objet pour éviter l’aliasing grâce au module copy.>>> p1 = Point()
>>> p1.x = 3.0
>>> p1.y = 4.0
>>> p2 = p1
>>> p2.y = 7.0
>>> print_point(p1)
(3, 7)
>>> p1 is p2
True
>>> import copy>>> p2 = copy.copy(p1)
>>> p2.y = 9.0
>>> print_point(p1)
(3, 7)
>>> print_point(p2)
(3, 9)
>>> p1 is p2
False
Programmation (Chap. 13) Introduction aux objets 13 / 48
Classes et objets
Copie d’objets
Soit :>>> p1 = Point()
>>> p1.x = 3.0
>>> p1.y = 4.0
>>> p2 = copy.copy(p1)
Que va retourner p1 == p2 ?
False !
Pour les objets, le comportement par défaut de == est le même quel’opérateur is. Mais ce comportement pourra être modifié (dans ladéfinition de classe).
Programmation (Chap. 13) Introduction aux objets 14 / 48
Classes et objets
Copie d’objets
Soit :>>> p1 = Point()
>>> p1.x = 3.0
>>> p1.y = 4.0
>>> p2 = copy.copy(p1)
Que va retourner p1 == p2 ?
False !
Pour les objets, le comportement par défaut de == est le même quel’opérateur is. Mais ce comportement pourra être modifié (dans ladéfinition de classe).
Programmation (Chap. 13) Introduction aux objets 14 / 48
Classes et objets
Copie d’objets
Soit :>>> box2 = copy.copy(box)
>>> box2 is box
False
Que va retourner box2.corner is box.corner ?
True !
La fonction copy.copy réalise une “shallow copy”, c-à-d qu’elle copieles références contenues dans les attributs mutables.
Programmation (Chap. 13) Introduction aux objets 15 / 48
Classes et objets
Copie d’objets
Soit :>>> box2 = copy.copy(box)
>>> box2 is box
False
Que va retourner box2.corner is box.corner ?
True !
La fonction copy.copy réalise une “shallow copy”, c-à-d qu’elle copieles références contenues dans les attributs mutables.
x
y
0.0
0.0
Pointwidth
height
corner
200.0
100.0
Rectangle
box
Rectangle
width 100.0
height 200.0
corner
box2
Programmation (Chap. 13) Introduction aux objets 15 / 48
Classes et objets
Copie d’objets
La fonction copy.deepcopy réalise une “deep copy”, c-à-d qu’elle copie“réellement” les objets référencés dans les attributs mutables (et ceuxréférencés par ceux-ci etc.).>>> box3 = copy.deepcopy(box)
>>> box3 is box
False
>>> box3.corner is box.corner
False
Les objets référencés par box et box3 sont maintenant complètementséparés (et indépendants).
Programmation (Chap. 13) Introduction aux objets 16 / 48
Fonctions pures
Fonctions pures
Programmation (Chap. 13) Introduction aux objets 17 / 48
Fonctions pures
Fonctions pures
Pour rappel, un effet de bord d’une fonction est quelque chose qu’elleproduit et qui est visible en dehors de celle-ci. Par ex. :
afficher une valeur;
demander une valeur à l’utilisateur;
écrire dans un fichier;
modifier un objet (mutable) passé en argument.
Retourner une valeur (return) n’est pas considéré comme un effet debord (intuition : boite noire).
Une fonction qui n’a pas d’effet de bord est parfois appelée unefonction pure. En général, on essaye toujours d’écrire des fonctionspures (sauf si le but de la fonction est explicitement d’appliquer uneffet de bord bien précis, par ex.: print_point).
Programmation (Chap. 13) Introduction aux objets 18 / 48
Fonctions pures
Fonctions pures
Exemple : on va définir une classe pour des objets représentant untemps (heure).class Time(object):
""" represente un temps (heure).attributs: hour, minute, second
"""
def print_time(t):
print ’%02d:%02d:%02d’ % (t.hour, t.minute, t.second)
Problème : écrire une fonction pure qui permet d’additionner deuxtemps.
Programmation (Chap. 13) Introduction aux objets 19 / 48
Fonctions pures
Fonctions pures
Premier prototype (ne tient pas compte du fait que 60 sec = 1min,etc.) :def add_time(t1, t2):
res = Time()
res.hour = t1.hour + t2.hour
res.minute = t1.minute + t2.minute
res.second = t1.second + t2.second
return res
C’est une fonction pure car elle ne modifie pas les deux temps passésen argument. Elle retourne un nouvel objet qui représente le tempstotal.
Programmation (Chap. 13) Introduction aux objets 20 / 48
Fonctions pures
Fonctions pures
>>> start = Time()
>>> start.hour = 9
>>> start.minute = 45
>>> start.second = 0
>>> duration = Time()
>>> duration.hour = 1
>>> duration.minute = 35
>>> duration.second = 0
>>> done = add_time(start, duration)
>>> print_time(start)
09:45:00
>>> print_time(duration)
01:35:00
>>> print_time(done)
10:80:00
Programmation (Chap. 13) Introduction aux objets 21 / 48
Fonctions pures
Fonctions pures
Deuxième prototype (tient compte des conversions mais code long) :def add_time(t1, t2):
res = Time()
res.hour = t1.hour + t2.hour
res.minute = t1.minute + t2.minute
res.second = t1.second + t2.second
if res.second >= 60:
res.second -= 60
res.minute += 1
if res.minute >= 60:
res.minute -= 60
res.hour += 1
return res
>>> done = add_time(start, duration)
>>> print_time(done)
11:20:00
Programmation (Chap. 13) Introduction aux objets 22 / 48
Fonctions pures
Fonctions pures
Pour améliorer le code de la fonction add_time, nous allons écrire desfonctions de conversions.def time_to_sec(time):
mins = time.hour * 60 + time.minute
secs = mins * 60 + time.second
return secs
def sec_to_time(secs):
time = Time()
mins, time.second = divmod(secs, 60)
time.hour, time.minute = divmod(mins, 60)
return time
def add_time(t1, t2):
secs = time_to_sec(t1) + time_to_sec(t2)
return sec_to_time(secs)
Programmation (Chap. 13) Introduction aux objets 23 / 48
Méthodes
Méthodes
Programmation (Chap. 13) Introduction aux objets 24 / 48
Méthodes
Méthodes
Les attributs d’un objet modélisent son état, ses particularités dans lafamille des objets du même type.Exemples :
un point a comme coordonnées (2,3); un autre (4,5), mais cesont deux points dans le plan.
un rectangle à une largeur de 10, une hauteur de 20, et un coininférieur droit en (3,7). Un autre sera plus grand, ou plus àdroite, mais il reste un rectangle.
un temps dans la journée correspond à la manière dont nousdécrivons un temps donné, c-à-d, une heure, une minute et uneseconde.
Mais un objet possède d’autres propriétés que ses attributs : lesméthodes associées à son type (sa classe).
Programmation (Chap. 13) Introduction aux objets 25 / 48
Méthodes
Méthodes
Une méthode est une fonction qui est définie pour un type donné etqui s’applique sur les objets de ce type.
Motivation : la plupart des fonctions définies dans les sectionsprécédentes (print_point, add_time, etc.) ont été définies pour untype d’objets particulier. Le principe des méthodes est de définirexplicitement ce type de fonctions dans la définition de la classeelle-même.
Intuition : une fonction est une boite noire qui utilise ses entrées pourproduire ses sorties. Une méthode est une action appliquée sur unobjet (et peut avoir besoin d’autres entrées que l’objet sur lequel elleest appliquée, et peut produire également des sorties).
Programmation (Chap. 13) Introduction aux objets 26 / 48
Méthodes
Méthodes
Pour afficher l’heure, nous avions défini une fonction :class Time(object):
""" represente un temps (heure).attributs: hour, minute, second
"""
def print_time(t):
print ’%02d:%02d:%02d’ % (t.hour, t.minute, t.second)
Cela nécessite de passer un objet Time en argument.>>> print_time(start)
09:45:00
Programmation (Chap. 13) Introduction aux objets 27 / 48
Méthodes
Méthodes
Comme la fonction print_time est destinée à être appliquéeuniquement sur des objets de type Time, nous voudrions plutôt latransformer en une méthode display spécifique à ces objets. Lasyntaxe, plus intuitive, deviendrait :>>> start.display()
09:45:00
Programmation (Chap. 13) Introduction aux objets 28 / 48
Méthodes
MéthodesDéfinir une méthode revient simplement à donner sa définition à l’intérieur dela définition de la classe. Pour accéder à l’objet sur lequel la méthode serainvoquée, on utilise la première variable nommée self (convention).
class Time(object):""" represente un temps (heure).
attributs: hour, minute, second"""def display(self):
print ’%02d:%02d:%02d’ % (self.hour, self.minute, self.second)
>>> start.display()
09:45:00
une méthode destinée à être invoquée sur un objet possède toujourscet objet comme premier paramètre;
quand on invoque la méthode sur un objet, il ne faut pas donnerl’argument (self);
ce type de méthodes est appelé une méthode d’instance;
∃ d’autres types de méthodes (de classes et statiques) qui ne serontpas vues dans ce cours.
Programmation (Chap. 13) Introduction aux objets 29 / 48
Méthodes
Méthodes
Intuition
“Hey print_time, here’s an object for you to print.”>>> print_time(start)
09:45:00
Programmation procédurale : les fonctions sont les agents actifs.
“Hey start, please display yourself.”>>> start.display()
09:45:00
Programmation orientée objets : les objets sont les agents actifs.
Programmation (Chap. 13) Introduction aux objets 30 / 48
Méthodes
Méthodes
Comment adapter la fonction add_time pour en faire une méthode ?
La somme de deux temps implique deux objets : l’un sera l’objet surlequel la méthode sera invoquée, l’autre sera donné en paramètre.
Le comportement sera un peu différent : add_time est une fonctionpure, ici nous allons écrire une méthode add qui va modifier l’objet surlequel elle est invoquée (rappel : ce comportement fonction pure /méthode modifiante est classique).
Pour cela nous allons d’abord réécrire la fonction time_to_sec en uneméthode to_sec.
De la même manière, le fonction sec_to_time sera transformée enune méthode update qui prend un nombre de secondes en argumentset met à jour l’objet Time en transformant ce nombre en temps.
Programmation (Chap. 13) Introduction aux objets 31 / 48
Méthodes
Méthodes
class Time(object):# ...def to_sec(self):
mins = self.hour * 60 + self.minute
secs = mins * 60 + self.second
return secs
def update(self, secs):
mins, self.second = divmod(secs, 60)
self.hour, self.minute = divmod(mins, 60)
def add(self, other):
secs = self.to_sec() + other.to_sec()
self.update(secs)
Programmation (Chap. 13) Introduction aux objets 32 / 48
Méthodes
Méthodes
>>> start = Time()
>>> start.hour = 9
>>> start.minute = 45
>>> start.second = 0
>>> duration = Time()
>>> duration.hour = 1
>>> duration.minute = 35
>>> duration.second = 0
>>> start.display()
09:45:00
>>> duration.display()
01:35:00
>>> duration.to_sec()
5700
>>> duration.update(5760)
>>> duration.display()
01:36:00
>>> start.add(duration)
>>> start.display()
11:21:00
Programmation (Chap. 13) Introduction aux objets 33 / 48
Méthodes
La méthode init
Jusqu’à présent, nous assignons les valeurs des attributs “à la main”.La méthode init (__init__) est une méthode spéciale qui est invoquéeautomatiquement quand un objet est créé. Cela permet d’ajouter desarguments (par défaut) quand on crée un objet.class Time(object):
# ...def __init__(self, hour=0, minute=0, second=0):
self.hour = hour
self.minute = minute
self.second = second
# ...
Programmation (Chap. 13) Introduction aux objets 34 / 48
Méthodes
La méthode init
>>> t1 = Time()
>>> t2 = Time(3)
>>> t3 = Time(4, 12)
>>> t4 = Time(18, 35, 17)
>>> t1.display()
00:00:00
>>> t2.display()
03:00:00
>>> t3.display()
04:12:00
>>> t4.display()
18:35:17
Programmation (Chap. 13) Introduction aux objets 35 / 48
Méthodes
La méthode str
La méthode str (__str__) est une méthode spéciale qui est censéeretournée une représentation de l’objet sous forme de chaîne decaractères. Lors d’une instruction print, Python appelleautomatiquement cette méthode.class Time(object):
# ...def __str__(self):
s = ’%02d:%02d:%02d’ % (self.hour, self.minute, self.second)
return s
# ...
Programmation (Chap. 13) Introduction aux objets 36 / 48
Méthodes
La méthode str
>>> t = Time(8,30)
>>> print t
08:30:00
>>> str(t)
’08:30:00’
la méthode display devient inutile.
__init__ et __str__ devraient toujours être présentes dans vosdéfinitions de classes (en général, on commence par cesméthodes).
Programmation (Chap. 13) Introduction aux objets 37 / 48
Méthodes
Surcharge d’opérateur
On peut également définir des opérateurs sur les objets. Par exemple,l’opérateur + peut être défini en écrivant une méthode spéciale__add__ pour la classe Time.
On parle de surcharge des opérateurs : adapter le comportement d’unopérateur à un type défini par le programmeur.class Time(object):
# ...def __add__(self, other):
res = Time()
secs = self.to_sec() + other.to_sec()
res.update(secs)
return res
# ...
Programmation (Chap. 13) Introduction aux objets 38 / 48
Méthodes
Surcharge d’opérateur
>>> t1 = Time(9)
>>> t2 = Time(1,30)
>>> t3 = t1 + t2
>>> print t3
10:30:00
Pour chaque opérateur présent en Python, il y a une méthode spécialequi correspond et qui permet de surcharger l’opérateur !
Voir: docs.python.org/ref/specialnames.html
Programmation (Chap. 13) Introduction aux objets 39 / 48
Méthodes
Surcharge d’opérateur
Problème : pour le moment l’opérateur + opère sur deux objets Time.Nous voudrions pouvoir également pouvoir ajouter un nombre entier(secondes) à ces objets.class Time(object):
# ...def __add__(self, other):
res = Time()
secs = self.to_sec()
if isinstance(other, Time):
secs += other.to_sec()
elif isinstance(other, int):
secs += other
else:raise NotImplemented(’op + only for Time and int’)
res.update(secs)
return res
# ...
Programmation (Chap. 13) Introduction aux objets 40 / 48
Méthodes
Surcharge d’opérateur
>>> t1 = Time(9)
>>> t2 = t1 + 600
>>> print t2
09:10:00
>>> t3 = t1 + t2
>>> print t3
18:10:00
Programmation (Chap. 13) Introduction aux objets 41 / 48
Méthodes
Surcharge d’opérateur
Problème : malheureusement, cette addition n’est pas commutative.Si l’entier est le premier opérateur, on obtient une erreur.>>> 1200 + t3
TypeError: unsupported operand type(s) for +: ’int’ and ’Time’
Le problème vient du fait qu’au lieu de demander à un objet Timed’ajouter un entier, on fait le contraire, et on ne peut pas modifier lesobjets de type entiers pour qu’ils connaissent les objets Time.
Solution : écrire la méthode spéciale __radd__ (right-side add). Cetteméthode est automatiquement invoquée quand un objet Time est lemembre droit d’un opérateur + (et que la méthode __add__ n’existepas ou retourne NotImplemented sur le membre gauche).
Programmation (Chap. 13) Introduction aux objets 42 / 48
Méthodes
Surcharge d’opérateur
class Time(object):# ...def __radd__(self, other):
return self.__add__(other)
# ...
>>> t1 = Time(8)
>>> t2 = 1200 + t1
>>> print t2
08:20:00
Remarque : les méthodes spéciales peuvent être également invoquée“à la main”, comme n’importe quelle autre méthode.
Programmation (Chap. 13) Introduction aux objets 43 / 48
Méthodes
Polymorphisme
On a déjà vu qu’une fonction peut s’appliquer sur différents typesd’objets, pour peu que le corps de la fonction applique des opérateursdisponibles pour ce type d’objet. On appelle cette caractéristique lepolymorphisme.
Cela signifie par exemple que l’on peut utiliser la fonction sum sur nosobjets Time !>>> sum(range(10))
45
>>> t = [Time(1,30), Time(2), Time(3,45,30)]
>>> total_time = sum(t)
>>> print total_time
07:15:30
Programmation (Chap. 13) Introduction aux objets 44 / 48
Programmation orientée objets
Programmation orientée objets
Programmation (Chap. 13) Introduction aux objets 45 / 48
Programmation orientée objets
Programmation orientée objets
Des langages comme Python, C++ ou Java permettent de définir desclasses, d’instancier des objets et de les manipuler.
Cette partie de la syntaxe n’est pas nécessaire (on pourrait tout fairerien qu’avec des fonctions), mais elle permet une programmationintuitive, concise et réutilisable.
Nous n’avons couvert qu’une toute petite partie des concepts orientésobjets. Il existe une série de techniques très intéressantes etpermettant d’accentuer encore les avantages précités.
Voir : cours “Langages et Algorithmique et Programmation I et II”.
Programmation (Chap. 13) Introduction aux objets 46 / 48
Glossaire
Glossaire
Programmation (Chap. 13) Introduction aux objets 47 / 48
Glossaire
Glossaire
classe : une définition de classe permet de définir un nouveau type d’objets.
instance : un objet (qui appartient à une classe).
attribut : une valeur (nommée) associée à un objet.
shallow copy : copie du contenu d’un objet, incluant les références vers d’autres objetsimbriqués (mais pas leur copie).
deep copy : copie du contenu d’un objet ainsi que des objets qui lui sont imbriqués.
fonction pure : une fonction qui ne produit pas d’effet de bord (et donc ne modifie pas lesobjets passés en arguments).
méthode d’instance : fonction définie à l’intérieur d’une définition de classe, et quis’applique sur les instances (objets) de cette classe.
Programmation (Chap. 13) Introduction aux objets 48 / 48
Chapitre 14. Introduction aux Structures de Données
Contenu du chapitre
1 Structure de Données
2 Listes chaînées
3 Piles
4 Files
5 Listes
Programmation (Chap. 14) Introduction aux Structures de Données 1 / 56
Structure de Données
Structure de Données
Programmation (Chap. 14) Introduction aux Structures de Données 2 / 56
Structure de Données
Structure de Données
Une structure de données permet :
de stocker des données;
de réaliser des opérations sur celles-ci.
Exemples : les listes, les tuples, les dictionnaires sont des structures
de données. Les méthodes et opérateurs applicables sur celles-ci
permettent de réaliser des opérations sur celles-ci (rechercher un
élément, ajouter un élément, obtenir le nombre d’éléments, etc.)
Dans ce cours nous allons voir 3 structures de données simples :
listes, piles et files.
On parle également de Type de Données Abstrait (TDA) car on sait ce
que cette structure peut faire et stocker, mais on ne sait pas commentles données sont organisées concrètement en interne (il peut y avoir
une multitude de manières d’obtenir le même comportement).
Programmation (Chap. 14) Introduction aux Structures de Données 3 / 56
Structure de Données
Liste
Une liste est un TDA permettant les opérations suivantes :
insérer un élément à un indice donné;
obtenir un élément à un indice donné;
obtenir le nombre d’éléments dans la liste;
afficher le contenu de la liste;
etc.
Exemple : les listes en Python
Programmation (Chap. 14) Introduction aux Structures de Données 4 / 56
Structure de Données
Pile
Une pile est un TDA permettant les opérations suivantes :
insérer un élément en haut de la pile;
obtenir (et retirer) l’élément du haut de la pile;
obtenir le nombre d’éléments dans la pile;
afficher le contenu de la pile;
etc.
Ce type de structure représente une pile car le dernier élément inséré
est le premier élément retiré (LIFO (Last In First Out)).
Fréquent en informatique : par ex. pile des fonctions appelées (fappelle g qui appelle h : h sera la première fonction terminée, puis on
retourne en g, etc.).
Programmation (Chap. 14) Introduction aux Structures de Données 5 / 56
Structure de Données
File
Une file est un TDA permettant les opérations suivantes :
insérer un élément à la fin de la file;
obtenir (et retirer) l’élément du début de la file;
obtenir le nombre d’éléments dans la file;
afficher le contenu de la file;
etc.
Ce type de structure représente une file car le premier élément inséré
est le premier élément retiré (FIFO (First In First Out)).
Fréquent en informatique : files de processus à accomplir, de fichiers
à traiter, etc.
Programmation (Chap. 14) Introduction aux Structures de Données 6 / 56
Structure de Données
Implémentation
Les opérations sur une structure de données sont la partie visible de
celles-ci. C’est ça qui intéresse l’utilisateur.
D’un point de vue “caché” (implémentation concrète), pour un TDA, il
peut y avoir beaucoup de possibilités :
choix de l’organisation interne des données (tuples, listes,
ensemble de variables, objets, etc.)
choix des algorithmes permettant d’effectuer les opérations
(dépend de la manière dont sont organisées les données).
Exemple : Pour créer une pile, on peut définir une classe Pile dont un des
attributs est une liste data (qui contient les données de la pile). Pour simuler
le fonctionnement de la pile, cette classe n’a qu’une méthode pour insérer un
élément : en le plaçant à la fin de data. De la même manière, elle n’a qu’une
méthode pour obtenir et retirer un élément : en choisissant le dernier élément
de data.
Programmation (Chap. 14) Introduction aux Structures de Données 7 / 56
Structure de Données
Implémentation
Dans ce chapitre, nous supposons que les listes, dictionnaires ou
autres structures de données prédéfinies en Python n’existent pas1.
Nous allons créer des classes permettant d’implémenter des listes,
des piles et des files, en essayant d’avoir une bonne complexité pour
chacune des opérations autorisées par chaque TDA.
D’un point de vue interne, nous allons organiser les données en
utilisant la notion de listes chaînées.
1Nous pourrons cependant utiliser des tuples au besoin, mais pas pour stocker les
données de manière interne.
Programmation (Chap. 14) Introduction aux Structures de Données 8 / 56
Listes chaînées
Listes chaînées
Programmation (Chap. 14) Introduction aux Structures de Données 9 / 56
Listes chaînées
Objets à taille variable ?
Nos 3 structures de données doivent permettre de stocker un nombre
indéfini d’objets et doivent être mutables.
Problème : comment déterminer les attributs des objets de type liste,
pile ou file, si nous ne connaissons pas le nombre d’éléments à
l’avance (et si les listes, dictionnaires, etc. n’existent pas en Python) ?
Programmation (Chap. 14) Introduction aux Structures de Données 10 / 56
Listes chaînées
Objets à taille variable ?
Ce qu’on ne peut pas faire : définir une classe ayant un nombre
indéfini d’attributs2.
Ce qui est autorisé : un attribut peut être une référence vers un autre
objet, de n’importe quel type.
Exemple : un Rectangle a un attribut Point
x
y
0.0
0.0
Pointwidth
height
corner
200.0
100.0
Rectangle
2ce n’est pas tout à fait vrai car les attributs d’un objet sont stockés dans un attribut
spécial __dict__ de type dictionnaire, mais c’est spécifique à Python.
Programmation (Chap. 14) Introduction aux Structures de Données 11 / 56
Listes chaînées
Objets à taille variable ?
Ce qui est autorisé : un attribut peut être une référence vers un autre
objet, de n’importe quel type, et donc y compris un autre objet de lamême classe.
Exemple : définissons une classe Node dont les objets auront deux attributs :
data : une donnée à stocker
next : une référence vers un autre Node s’il y a un élément suivant, ou
None sinon.
class Node(object):""" represente un maillon d’une liste chainee
attributs: data (donnee) et next_ (maillon suivant)
"""
def __init__(self, data = 0, next_ = None):self.data = dataself.next = next_
p = Node()Node
next
0data
None
p
Programmation (Chap. 14) Introduction aux Structures de Données 12 / 56
Listes chaînées
Liste chaînées
Une liste chaînée est constituée de maillons. Chaque maillon (ou
noeud) possède deux attributs : une donnée et une référence vers
l’élément suivant.
Notation : dans nos schémas, on utilise * pour représenter None
Elements dans la liste : 0
*0
Node
next
0data
None
p
p = Node()
Programmation (Chap. 14) Introduction aux Structures de Données 13 / 56
Listes chaînées
Liste chaînées
Une liste chaînée est constituée de maillons. Chaque maillon (ou
noeud) possède deux attributs : une donnée et une référence vers
l’élément suivant.
Notation : dans nos schémas, on utilise * pour représenter None
Elements dans la liste : 0,1
0 *1
Node
next
1
None
data
Node
next
0data
p q
q = Node(1)p.next = q
Programmation (Chap. 14) Introduction aux Structures de Données 14 / 56
Listes chaînées
Liste chaînées
Une liste chaînée est constituée de maillons. Chaque maillon (ou
noeud) possède deux attributs : une donnée et une référence vers
l’élément suivant.
Notation : dans nos schémas, on utilise * pour représenter None
Elements dans la liste : 0,1,2
0 *12
Node
next
1
None
data
Node
next
2data
r Node
next
0data
p q
r = Node(2, p)
Programmation (Chap. 14) Introduction aux Structures de Données 15 / 56
Listes chaînées
Listes chaînées
Pour afficher le contenu d’une liste chaînée, on peut écrire une
fonction qui prend en paramètre un Node, affiche sa donnée, et
continue à afficher les données tant que l’élément suivant n’est pas
None. On utilise une variable p pour référencer le maillon courant.
def print_list(t):p = twhile p != None:
print p.data, ’->’,p = p.next
print ’*’
>>> print_list(p)0 -> 1 -> *>>> print_list(q)1 -> *>>> print_list(r)2 -> 0 -> 1 -> *
Programmation (Chap. 14) Introduction aux Structures de Données 16 / 56
Listes chaînées
Listes chaînées
Travailler avec des listes chaînées revient donc à utiliser 5 opérations
de base :
création d’un élément : créer un nouveau maillon via Node(...)
mise à jour d’une donnée : assigner une référence data via une
affectation (par ex. p.data = 4 ou en passant un premier
paramètre à Node(data, next))
mise à jour de la structure : assigner une référence next via une
affectation (par ex. p.next = q ou en passant un second
paramètre à Node(data, next))
référencer un élément : assigner une référence vers un via une
affectation (par ex. first = r)
Programmation (Chap. 14) Introduction aux Structures de Données 17 / 56
Listes chaînées
Listes chaînées
Chacune de ces quatre opérations peut-être vue comme une
modification du schéma de la liste.
création d’un élément : dessiner un nouveau carré représentant
un maillon;
mise à jour d’une donnée : mettre une nouvelle valeur dans un
carré;
mise à jour de la structure : (re) dessiner une flêche d’un carré
vers un autre (ou vers *);
référencer un élément : dessiner une flêche vers un carré.
Programmation (Chap. 14) Introduction aux Structures de Données 18 / 56
Listes chaînées
Listes chaînées
Exemple : pour passer de*0
p
à0 *1
qp
:
on crée le nouveau maillon, on lui affecte les valeurs 1 (data) et
None (next), et on assigne la référence vers ce nouveau maillon à
q :
q = Node(1)
on met à jour la “flêche” (= l’attribut next) du maillon référencé
par p :
p.next = q
Cette dernière instruction peut être lue comme : mettre à jour la flêchede p et la faire pointer vers ce que pointe q.
D’un point de vue machine, la valeur de q est une adresse en mémoire
(l’adresse de l’endroit où est stocké le second maillon). Nous voulons que pconnaisse l’adresse en mémoire de l’élément suivant et nous la stockons
donc dans p.next.
Programmation (Chap. 14) Introduction aux Structures de Données 19 / 56
Listes chaînées
Listes chaînées
Exemple :
def print_list(t):p = twhile p != None:
print p.data, ’->’,p = p.next
print ’*’
>>> print_list(r)2 -> 0 -> 1 -> *
Dans cette fonction, p est une “flêche” vers, d’abord, le maillon passé
en argument et référencé par t, puis est mis à jour pour pointer vers
l’élément suivant et ainsi de suite.
*2
p
0 *12
p
p
0 *12
0 1
Programmation (Chap. 14) Introduction aux Structures de Données 20 / 56
Listes chaînées
Listes chaînées
Exercice : comment insérer 3 entre 2 et 0 ? On suppose qu’on a
seulement une référence first vers le premier élément.
Solution :
0 *12
first
1 création nouveau maillon référencé par p : p = Node(3)
2 mise à jour du suivant de p : p.next = first.next
3 mise à jour du suivant de first : first.next = p
L’ordre est important ! (voyez-vous pourquoi ?)
Programmation (Chap. 14) Introduction aux Structures de Données 21 / 56
Listes chaînées
Listes chaînées
Exercice : comment insérer 3 entre 2 et 0 ? On suppose qu’on a
seulement une référence first vers le premier élément.
Solution :
0 *12
first
3
p
*
1 création nouveau maillon référencé par p : p = Node(3)
2 mise à jour du suivant de p : p.next = first.next
3 mise à jour du suivant de first : first.next = p
L’ordre est important ! (voyez-vous pourquoi ?)
Programmation (Chap. 14) Introduction aux Structures de Données 21 / 56
Listes chaînées
Listes chaînées
Exercice : comment insérer 3 entre 2 et 0 ? On suppose qu’on a
seulement une référence first vers le premier élément.
Solution :
0 *12
first
3
p
1 création nouveau maillon référencé par p : p = Node(3)
2 mise à jour du suivant de p : p.next = first.next
3 mise à jour du suivant de first : first.next = p
L’ordre est important ! (voyez-vous pourquoi ?)
Programmation (Chap. 14) Introduction aux Structures de Données 21 / 56
Listes chaînées
Listes chaînées
Exercice : comment insérer 3 entre 2 et 0 ? On suppose qu’on a
seulement une référence first vers le premier élément.
Solution :
0 *12
first
3
p
1 création nouveau maillon référencé par p : p = Node(3)
2 mise à jour du suivant de p : p.next = first.next
3 mise à jour du suivant de first : first.next = p
L’ordre est important ! (voyez-vous pourquoi ?)
Programmation (Chap. 14) Introduction aux Structures de Données 21 / 56
Listes chaînées
Listes chaînées
Exercice : comment supprimer l’élément 0 de la liste ?
Solution :
0 *12
first
1 mise à jour du suivant de first :
first.next = first.next.next
Cela suffit ! Le maillon “0” est déconnecté et inaccessible
puisqu’aucune référence ne pointe plus vers lui.
Programmation (Chap. 14) Introduction aux Structures de Données 22 / 56
Listes chaînées
Listes chaînées
Exercice : comment supprimer l’élément 0 de la liste ?
Solution :
0 *12
first
1 mise à jour du suivant de first :
first.next = first.next.next
Cela suffit ! Le maillon “0” est déconnecté et inaccessible
puisqu’aucune référence ne pointe plus vers lui.
Programmation (Chap. 14) Introduction aux Structures de Données 22 / 56
Listes chaînées
Listes chaînées
Les listes chaînées permettent donc de créer des structures de
données de tailles variables.
Nous allons les utiliser pour implémenter les piles, les files et les listes.
La classe de base représentant un maillon d’une chaîne sera la
suivante (dans un module node.py disponible sur e-learning).
Remarque : La méthode spéciale __str__ est appelée
automatiquement via la fonction str ou une instruction print; la
méthode spéciale __repr__ est appelée automatiquement via la
fonction repr ou quand on donne une variable simple dans la console
interactive.
Programmation (Chap. 14) Introduction aux Structures de Données 23 / 56
Listes chaînées
Listes chaînées
class Node(object):""" represente un maillon d’une liste chainee
attributs: data (donnee) et next (maillon suivant)
"""
def __init__(self, data = 0, next_ = None):self.data = dataself.next = next_
def __str__(self):return str(self.data)
def has_next(self):""" retourne vrai ssi ce maillon a un maillon suivant """
return self.next != None
Programmation (Chap. 14) Introduction aux Structures de Données 24 / 56
Listes chaînées
Listes chaînées
(suite)
def __repr__(self):res = ’’p = selfwhile p != None:
res += str(p) + ’ -> ’p = p.next
res += ’*’return res
>>> p = Node(3, Node(4))>>> p3 -> 4 -> *>>> print p3>>> print p.next4>>> p.has_next()True>>> p.next.has_next()False
Programmation (Chap. 14) Introduction aux Structures de Données 25 / 56
Piles
Piles
Programmation (Chap. 14) Introduction aux Structures de Données 26 / 56
Piles
Piles
Nous désirons les opérations (méthodes) suivantes, avec la meilleure
complexité possible, et en utilisant une liste chaînée en interne :
is_empty() : retourne vrai ssi la pile est vide;
count() : retourne le nombre d’éléments dans la pile;
head() : retourne l’élément en haut de la pile (sans le retirer);
pop() : retourne l’élément en haut de la pile et le retire de celle-ci;
push(x) : insère x en haut de la pile.
display() : affiche le contenu de la pile.
clear() : vide la pile.
__init__() : initialise la pile (vide par défaut) ou avec un nombre
d’éléments variables.
Pour rendre nos piles plus facilement utilisables, nous allons
également associer certaines des méthodes spéciales à nos
méthodes (par ex. __str__ et display).
Programmation (Chap. 14) Introduction aux Structures de Données 27 / 56
Piles
Piles
Attributs : une référence vers le premier maillon d’une liste chaînée et
le nombre n d’éléments dans la pile (pour éviter de traverser la pile
pour compter le nombre d’éléments).
Idée : le premier élément de la liste chaînée sera l’élément en “haut”
de la pile, ce qui permettra d’y travailler en O(1), sans devoir parcourir
la liste jusqu’à la fin.
from node import Node
class Pile(object):""" represente une pile (LIFO) """
def __init__(self):self.top = None
Pour le moment, l’attribut top ne référencie aucun Node : la pile est
vide. On améliorera __init__ par après (ajouter un nombre variable
d’éléments).
Programmation (Chap. 14) Introduction aux Structures de Données 28 / 56
Piles
Piles
Méthode push : Il faut insérer un élément au début de la liste chaînée.
class Pile(object):# ...
def push(self, x):p = Node(x)p.next = self.topself.top = pself.n += 1
>>> stack = Pile()>>> stack.push(2)>>> stack.push(4)>>> stack.push(3)>>> stack.top3 -> 4 -> 2 -> *>>> print stack.n3
top
p*
top *
p
top
...
24 *
3
2
Programmation (Chap. 14) Introduction aux Structures de Données 29 / 56
Piles
Piles
Méthodes is_empty et count. La méthode spéciale __len__ sera
appelée automatiquement via la fonction len.
class Pile(object):#...
def is_empty(self):return self.top == None
def count(self):return self.n
def __len__(self):return self.count()
>>> stack = Pile()>>> stack.is_empty()True>>> stack.push(2)>>> stack.is_empty()False>>> stack.count()1>>> stack.push(3)>>> len(stack)2
Programmation (Chap. 14) Introduction aux Structures de Données 30 / 56
Piles
Piles
Méthodes d’affichage.
class Pile(object):# ...
def __str__(self):res = ’’p = self.topwhile p != None:
if not p is self.top:res += ’\n’
res += str(p)p = p.next
return res
def display(self):print self
def __repr__(self):return repr(self.top)
>>> stack = Pile()>>> stack.push(1)>>> stack.push(2)>>> stack.push(3)>>> print stack321>>> stack.display()321>>> stack3 -> 2 -> 1 -> *
Programmation (Chap. 14) Introduction aux Structures de Données 31 / 56
Piles
Piles
Avant d’implémenter top et pop, nous aimerions pouvoir exprimer le
fait que ces méthodes doivent être appelées sur des piles non vides.
Pour ce faire, nous allons définir un nouveau type d’exception.
Pour créer un nouveau type d’exception la syntaxe est simple : les
exceptions sont des objets de type Exception. On peut le faire de la
manière suivante (on inclut ceci dans le module node.py pour que ce
soit également utilisable pour les files et les listes).
Il n’y a pas besoin de définir des attributs ou des méthodes pour une
exception.
class EmptyError(Exception):pass
Programmation (Chap. 14) Introduction aux Structures de Données 32 / 56
Piles
Piles
Méthodes head et pop.
#...
from node import EmptyError
class Pile(object):#...
def head(self):if self.is_empty():
raise EmptyError(’Stack is empty’)return self.top.data
def pop(self):if self.is_empty():
raise EmptyError(’Stack is empty’)res = self.top.dataself.top = self.top.nextself.n -= 1return res
>>> stack.push(1)>>> stack.push(2)>>> stack.push(3)>>> stack.head()3>>> res = stack.pop()>>> print stack21>>> res3>>> stack.pop()2>>> stack.pop()1>>> stack.pop()node.EmptyError: Stack is empty
Programmation (Chap. 14) Introduction aux Structures de Données 33 / 56
Piles
Piles
Méthodes clear et __init__ (revisitée).
class Pile(object):def __init__(self, *args):
self.top = Noneself.n = 0for i in args:
self.push(i)
def clear(self):self.top = Noneself.n = 0
# ...
>>> stack = Pile(1, 2, 3)>>> stack3 -> 2 -> 1 -> *>>> stack.pop()3>>> len(stack)2>>> stack.clear()>>> len(stack)0>>> stack.is_empty()True
Programmation (Chap. 14) Introduction aux Structures de Données 34 / 56
Piles
Piles
Le code complet de la classe Pile est disponible sur e-learning.
Complexité
les méthodes d’affichage sont en O(n) où n est le nombre
d’éléments de la pile;
la méthode __init__ est en O(k) où k est le nombre
d’arguments passés;
toutes les autres méthodes sont en O(1) grâce à l’attribut n et
l’insertion / suppression au début de la liste chaînée.
On ne pourrait pas faire mieux !
Programmation (Chap. 14) Introduction aux Structures de Données 35 / 56
Piles
Piles
Application : le script suivant montre une utilisation des piles pour
montrer la pile des fonctions appelées lors d’appels récursifs.
from pile import Pile
f_stack = Pile()
def somme(n):msg = ’call somme(’ + str(n) + ’)’f_stack.push(msg)if n <= 1:
res = nprint ’Basis reached. Current stack:’print f_stackprint ’-----------------------------’
else:res = n + somme(n-1)
done = f_stack.pop()print done + ’ is finished’return res
somme(4)
Programmation (Chap. 14) Introduction aux Structures de Données 36 / 56
Piles
Piles
Application : output
Basis reached. Current stack:call somme(1)call somme(2)call somme(3)call somme(4)-----------------------------call somme(1) is finishedcall somme(2) is finishedcall somme(3) is finishedcall somme(4) is finished
Programmation (Chap. 14) Introduction aux Structures de Données 37 / 56
Files
Files
Programmation (Chap. 14) Introduction aux Structures de Données 38 / 56
Files
Files
Nous désirons les opérations (méthodes) suivantes, avec la meilleure
complexité possible, et en utilisant une liste chaînée en interne :
is_empty() : retourne vrai ssi la file est vide;
count() : retourne le nombre d’éléments dans la file;
head() : retourne l’élément au début de la file (sans le retirer);
remove() : retourne l’élément au début de la file et le retire de
celle-ci;
insert(x) : insère x en fin de file.
display() : affiche le contenu de la file.
clear() : vide la file.
__init__() : initialise la file (vide par défaut) ou avec un nombre
d’éléments variables.
Programmation (Chap. 14) Introduction aux Structures de Données 39 / 56
Files
Files
Attributs : une référence vers le premier maillon de la liste chaînée,
une référence vers le dernier maillon de la liste chaînée et le nombre nd’éléments dans la file.
Idée : le premier élément de la liste chaînée sera l’élément au début
de la file (first), ce qui permettra d’y travailler en O(1). On conserve
également une référence vers le dernier élément de la liste chaînée
pour pouvoir insérer un nouvel élément en O(1) (last).
Programmation (Chap. 14) Introduction aux Structures de Données 40 / 56
Files
Files
Module file.py
from node import Nodefrom node import EmptyError
class File(object):""" represente une file (FIFO) """
def __init__(self, *args):self.first = Noneself.last = Noneself.n = 0for i in args:
self.insert(i)
def clear(self):self.first = Noneself.last = Noneself.n = 0
Programmation (Chap. 14) Introduction aux Structures de Données 41 / 56
Files
Files
(suite)
def insert(self, x):p = Node(x)if self.is_empty():
self.first = pself.last = p
else:self.last.next = pself.last = p
self.n += 1
def head(self):if self.is_empty():
raise EmptyError(’Queue is empty’)return self.first.data
Programmation (Chap. 14) Introduction aux Structures de Données 42 / 56
Files
Files
(suite)
def remove(self):if self.is_empty():
raise EmptyError(’Queue is empty’)res = self.first.dataself.first = self.first.nextself.n -= 1return res
def is_empty(self):return self.first == None
def count(self):return self.n
def __len__(self):return self.count()
Programmation (Chap. 14) Introduction aux Structures de Données 43 / 56
Files
Files
(suite)
def __str__(self):res = ’’p = self.firstwhile p != None:
if not p is self.first:res += ’ < ’
res += str(p)p = p.next
return res
def __repr__(self):return repr(self.first)
def display(self):print self
Programmation (Chap. 14) Introduction aux Structures de Données 44 / 56
Files
Files
>>> queue = File(1, 2, 3)>>> print queue1 < 2 < 3>>> queue1 -> 2 -> 3 -> *>>> len(queue)3>>> queue.insert(4)>>> queue.head()1>>> queue.remove()1>>> queue.remove()2>>> queue.clear()>>> queue.remove()node.EmptyError: Queue is empty
Programmation (Chap. 14) Introduction aux Structures de Données 45 / 56
Files
Files
Le code complet de la classe File est disponible sur e-learning.
Complexité
les méthodes d’affichage sont en O(n) où n est le nombre
d’éléments de la pile;
la méthode __init__ est en O(k) où k est le nombre
d’arguments passés;
toutes les autres méthodes sont en O(1) grâce à l’attribut n et les
deux références permettant de faire l’insertion et la suppression
en O(1).
Ici aussi, on ne pourrait pas faire mieux.
Programmation (Chap. 14) Introduction aux Structures de Données 46 / 56
Files
Files
Application : le script suivant montre une utilisation des files pour
simuler la file d’attente d’un bureau administratif. Chaque minute, il y a
1 chance sur 5 qu’un nouveau client arrive (ce qui fait en moyenne 12
clients par heure). Le responsable du guichet prend entre 5 et 15
minutes pour servir un client. Le bureau est ouvert de 8 à 12h et de
13h à 16h. A l’heure de fermeture, les clients encore présents dans la
file quittent le bureau sans avoir été servis.
Comment simuler cette situation pour estimer le nombre de clients
mécontents ?
Nous utilisons la classe Time définie au Chapitre 13, pour laquelle on a
ajouté une surcharge de l’opérateur <=.
Programmation (Chap. 14) Introduction aux Structures de Données 47 / 56
Files
Files
from random import randintfrom random import choicefrom time import Timefrom file import File
class Someone(object):def __init__(self):
self.name = self.random_firstname() + ’ ’ + self.random_lastname()
def random_firstname(self):firstnames = (’Bill’, ’Joe’, ’Jack’, ’Georges’, ’Alan’, ’Lisa’,
’Sarah’, ’Kate’, ’Charley’)return choice(firstnames)
def random_lastname(self):lastnames = (’Black’, ’Baroud’, ’Cormen’, ’Rivest’, ’Aho’,
’Ullman’, ’Turing’, ’Escher’)return choice(lastnames)
def __str__(self):return self.name
Programmation (Chap. 14) Introduction aux Structures de Données 48 / 56
Files
Files
(suite)
class Simulation(object):def __init__(self, start, end):
self.serving = Falseself.minUntilDone = 0self.wait_queue = File()self.clock = startself.end = end
def has_new_client(self):return randint(1, 5) == 1
def random_servetime(self):return randint(5,15)
def is_finished(self):return self.clock >= self.end
Programmation (Chap. 14) Introduction aux Structures de Données 49 / 56
Files
Files
(suite, toujours dans la classe Simulation)
def simulate_one_minute(self):if self.has_new_client():
new = Someone()print self.clock, ’:’, new, ’just arrived’self.wait_queue.insert(new)
if self.serving:if self.minUntilDone == 0:
self.serving = Falseold = self.wait_queue.remove()print self.clock, ’:’, old ,’is happy and leaving office’
else:self.minUntilDone -= 1
else:if not self.wait_queue.is_empty():
self.serving = Trueself.minUntilDone = self.random_servetime()print self.clock, ’:’, self.wait_queue.head() ,print ’is currently served’
self.clock = self.clock + 60
Programmation (Chap. 14) Introduction aux Structures de Données 50 / 56
Files
Files
(suite, toujours dans la classe Simulation)
def open_office(self):print ’It is’, self.clock, ’the office is open!’
def close_office(self):print ’It is’, self.clock, ’the office is closed!’print ’There are’, len(self.wait_queue),’people in the queue that are’,print ’in very bad mood.’self.wait_queue.clear()self.serving = False
Programmation (Chap. 14) Introduction aux Structures de Données 51 / 56
Files
Files
(suite, code principal)
sim = Simulation(Time(8), Time(12))sim.open_office()while not sim.is_finished():
sim.simulate_one_minute()sim.close_office()
sim = Simulation(Time(13), Time(16))sim.open_office()while not sim.is_finished():
sim.simulate_one_minute()sim.close_office()
Programmation (Chap. 14) Introduction aux Structures de Données 52 / 56
Files
Files
Application : output
It is 08:00:00 the office is open!08:00:00 : Bill Black just arrived08:00:00 : Bill Black is currently served08:02:00 : Georges Escher just arrived08:08:00 : Alan Cormen just arrived08:09:00 : Georges Ullman just arrived08:10:00 : Joe Ullman just arrived08:10:00 : Bill Black is happy and leaving office08:10:00 : Georges Escher is currently served...It is 12:00:00 the office is closed!There are 27 people in the queue that are in very bad mood.It is 13:00:00 the office is open!13:03:00 : Joe Cormen just arrived13:03:00 : Joe Cormen is currently served13:08:00 : Joe Cormen is happy and leaving office13:10:00 : Sarah Ullman just arrived13:10:00 : Sarah Ullman is currently served13:13:00 : Georges Cormen just arrived13:15:00 : Kate Cormen just arrived13:24:00 : Sarah Ullman is happy and leaving office13:24:00 : Georges Cormen is currently served13:26:00 : Alan Rivest just arrived13:36:00 : Charley Rivest just arrived13:38:00 : Georges Cormen is happy and leaving office13:38:00 : Kate Cormen is currently served...It is 16:00:00 the office is closed!There are 16 people in the queue that are in very bad mood.
Programmation (Chap. 14) Introduction aux Structures de Données 53 / 56
Listes
Listes
Programmation (Chap. 14) Introduction aux Structures de Données 54 / 56
Listes
Listes
Nous désirons les opérations (méthodes) suivantes, et en utilisant une
liste chaînée en interne :
is_empty() : retourne vrai ssi la liste est vide;
count() : retourne le nombre d’éléments dans la liste;
remove(i) : retourne et supprime l’élément qui se trouve à
l’indice i ;insert(x [, i]) : insère x à la fin de la liste si i n’est pas
présent, ou entre les élements d’indice i et i +1 si i est précisé;
display() : affiche le contenu de la liste.
clear() : vide la liste.
__init__() : initialise la liste (vide par défaut) ou avec un nombre
d’éléments variables.
+ : concatène deux listes (surcharge de __add(self, other)__)
[i] : retourne l’élément d’indice i (surcharge de
__getitem__(self, index))
Programmation (Chap. 14) Introduction aux Structures de Données 55 / 56
Listes
Listes
A vous de jouer !
Préparez une classe Liste qui implémente les opérations
demandées:
en essayant d’avoir une bonne complexité (mais accès à un
indice donné ne sera pas en O(1));
en lançant des exceptions EmptyError ou IndexError quand
nécessaire.
A préparer pour le prochain TP.
Programmation (Chap. 14) Introduction aux Structures de Données 56 / 56