Vous êtes sur la page 1sur 473

Pages de variables

Document = Apprendre programmer avec Python


NumeroExercice = 0
caractres invisibles blancs :ttt
Point est une variable qui sert afficher/masquer un point dans le titre courant de la page de droite : .

La version numrique de ce texte peut tre tlcharge librement partir du site :


http://inforef.be/swi/python.htm
Quelques paragraphes de cet ouvrage ont t adapts de :
How to think like a computer scientist
de Allen B. Downey, Jeffrey Elkner & Chris Meyers
disponible sur : http://thinkpython.com
ou : http://www.openbookproject.net/thinkCSpy

Copyright (C) 2000-2012 Grard Swinnen


L'ouvrage qui suit est distribu suivant les termes de la Licence Creative Commons Paternit-Pas
d'Utilisation Commerciale-Partage des Conditions Initiales l'Identique - 2.0 France .
Cela signifie que vous pouvez copier, modifier et redistribuer ces pages tout fait librement, pour
autant que vous respectiez un certain nombre de rgles qui sont prcises dans cette licence, dont le
texte complet peut tre consult dans l'annexe C, page 445.
Pour l'essentiel, sachez que vous ne pouvez pas vous approprier ce texte pour le redistribuer ensuite
(modifi ou non) en dfinissant vous-mme d'autres droits de copie. Le document que vous
redistribuez, modifi ou non, doit obligatoirement inclure intgralement le texte de la licence cite
ci-dessus, le prsent avis et la prface qui suit. L'accs ces notes doit rester libre pour tout le monde.
Vous tes autoris demander une contribution financire ceux qui vous redistribuez ces notes,
mais la somme demande ne peut concerner que les frais de reproduction. Vous ne pouvez pas
redistribuer ces notes en exigeant pour vous-mme des droits d'auteur, ni limiter les droits de
reproduction des copies que vous distribuez. La diffusion commerciale de ce texte en librairie, sous la
forme classique d'un manuel imprim, est rserve exclusivement la maison d'dition Eyrolles
(Paris).
La couverture
Choisie dlibrment hors propos, lillustration de couverture est la reproduction dune uvre lhuile ralise
par lauteur daprs une gravure de J.J. Baujean. Elle met en scne un petit sloop de cabotage de la fin du 18 e sicle.
Ces btiments de 60 80 tonneaux possdaient une grande voile de fortune, utilise par vent arrire comme on le
voit ici, ainsi quun hunier pour les plus grands dentre eux.

Grace Hopper, inventeur du compilateur :


Pour moi, la programmation est plus quun art appliqu important. Cest aussi une ambitieuse qute mene
dans les trfonds de la connaissance.

Maximilien, lise, Lucille, Augustin et Alexane.

Prface
En tant que professeur ayant pratiqu lenseignement de la programmation en parallle avec dautres
disciplines, je crois pouvoir affirmer quil sagit l dune forme dapprentissage extrmement
enrichissante pour la formation intellectuelle dun jeune, et dont la valeur formative est au moins
gale, sinon suprieure, celle de branches plus classiques telles que le latin.
Excellente ide donc, que celle de proposer cet apprentissage dans certaines filires, y compris de
lenseignement secondaire. Comprenons-nous bien : il ne sagit pas de former trop prcocement de
futurs programmeurs professionnels. Nous sommes simplement convaincus que lapprentissage de la
programmation a sa place dans la formation gnrale des jeunes (ou au moins dune partie dentre
eux), car cest une extraordinaire cole de logique, de rigueur, et mme de courage.
lorigine, le prsent ouvrage a t rdig lintention des lves qui suivent le cours Programmation
et langages de loption Sciences & informatique au 3e degr de lenseignement secondaire belge. Il nous a
sembl par la suite que ce cours pouvait galement convenir toute personne nayant encore jamais
programm, mais souhaitant sinitier cette discipline en autodidacte.
Nous y proposons une dmarche dapprentissage non linaire qui est trs certainement critiquable.
Nous sommes conscients quelle apparatra un peu chaotique aux yeux de certains puristes, mais nous
lavons voulue ainsi parce que nous sommes convaincus quil existe de nombreuses manires
dapprendre (pas seulement la programmation, dailleurs), et quil faut accepter demble ce fait tabli
que des individus diffrents nassimilent pas les mmes concepts dans le mme ordre. Nous avons
donc cherch avant tout susciter lintrt et ouvrir un maximum de portes, en nous efforant tout
de mme de respecter les principes directeurs suivants :

Lapprentissage que nous visons se veut gnraliste : nous souhaitons mettre en vidence les
invariants de la programmation et de linformatique, sans nous laisser entraner vers une
spcialisation quelconque, ni supposer que le lecteur dispose de capacits intellectuelles hors du
commun.
Les outils utiliss au cours de lapprentissage doivent tre modernes et performants, mais il faut
aussi que le lecteur puisse se les procurer en toute lgalit trs bas prix pour son usage
personnel. Notre texte sadresse en effet en priorit des tudiants, et toute notre dmarche
dapprentissage vise leur donner la possibilit de mettre en chantier le plus tt possible des
ralisations personnelles quils pourront dvelopper et exploiter leur guise.
Nous aborderons trs tt la programmation dune interface graphique, avant mme davoir
prsent lensemble des structures de donnes disponibles, parce que cette programmation
prsente des dfis qui apparaissent concrtement aux yeux dun programmeur dbutant. Nous ob-

VI
servons par ailleurs que les jeunes qui arrivent aujourdhui dans nos classes baignent dj dans
une culture informatique base de fentres et autres objets graphiques interactifs. Sils
choisissent dapprendre la programmation, ils sont forcment impatients de crer par eux-mmes
des applications (peut-tre trs simples) o laspect graphique est dj bien prsent. Nous avons
donc choisi cette approche un peu inhabituelle afin de permettre au lecteur de se lancer trs tt
dans de petits projets personnels attrayants, par lesquels il puisse se sentir valoris. En revanche,
nous laisserons dlibrment de ct les environnements de programmation sophistiqus qui
crivent automatiquement de nombreuses lignes de code, parce que nous ne voulons pas non plus
masquer la complexit sous-jacente.
Certains nous reprocheront que notre dmarche nest pas suffisamment centre sur lalgorithmique
pure et dure. Nous pensons que celle-ci est moins primordiale que par le pass. Il semble en effet que
lapprentissage de la programmation moderne par objets ncessite plutt une mise en contact aussi
prcoce que possible de lapprenant avec des objets et des bibliothques de classes prexistants. Ainsi,
il apprend trs tt raisonner en termes dinteractions entre objets, plutt quen termes de
construction de procdures, et cela lautorise assez vite tirer profit de concepts avancs, tels que
linstanciation, lhritage et le polymorphisme.
Nous avons par ailleurs accord une place assez importante la manipulation de diffrents types de
structures de donnes, car nous estimons que cest la rflexion sur les donnes qui doit rester la
colonne vertbrale de tout dveloppement logiciel.

Choix dun premier langage de programmation


Il existe un trs grand nombre de langages de programmation, chacun avec ses avantages et ses
inconvnients. Il faut bien en choisir un. Lorsque nous avons commenc rflchir cette question,
durant notre prparation dun curriculum pour la nouvelle option Sciences & Informatique, nous
avions personnellement accumul une assez longue exprience de la programmation sous Visual Basic
(Microsoft) et sous Clarion (Topspeed). Nous avions galement expriment quelque peu sous Delphi (Borland). Il tait donc naturel que nous pensions dabord exploiter lun ou lautre de ces langages. Si nous
souhaitions les utiliser comme outils de base pour un apprentissage gnral de la programmation, ces
langages prsentaient toutefois deux gros inconvnients :

Ils sont lis des environnements de programmation (cest--dire des logiciels) propritaires.
Cela signifiait donc, non seulement que linstitution scolaire dsireuse de les utiliser devrait
acheter une licence de ces logiciels pour chaque poste de travail (ce qui pouvait se rvler coteux
), mais surtout que les lves souhaitant utiliser leurs comptences de programmation ailleurs
qu lcole seraient implicitement forcs dacqurir eux aussi des licences, ce que nous ne
pouvions pas accepter. Un autre grave inconvnient de ces produits propritaires est quils
comportent de nombreuses botes noires dont on ne peut connatre le contenu. Leur
documentation est donc incomplte, et leur volution incertaine.
Ce sont des langages spcifiquement lis au seul systme dexploitation Windows. Ils ne sont pas
portables sur dautres systmes (Unix, Mac OS, etc.). Cela ne cadrait pas avec notre projet
pdagogique qui ambitionne dinculquer une formation gnrale (et donc diversifie) dans
laquelle les invariants de linformatique seraient autant que possible mis en vidence.

VII
Nous avons alors dcid dexaminer loffre alternative, cest--dire celle qui est propose gratuitement
dans la mouvance de linformatique libre1. Ce que nous avons trouv nous a enthousiasms : non seulement il existe dans le monde de lOpen Source des interprteurs et des compilateurs gratuits pour toute
une srie de langages, mais surtout ces langages sont modernes, performants, portables (cest--dire
utilisables sur diffrents systmes dexploitation tels que Windows, Linux, Mac OS ...), et fort bien
documents.
Le langage dominant y est sans conteste C/C++. Ce langage simpose comme une rfrence absolue, et
tout informaticien srieux doit sy frotter tt ou tard. Il est malheureusement trs rbarbatif et
compliqu, trop proche de la machine. Sa syntaxe est peu lisible et fort contraignante. La mise au
point dun gros logiciel crit en C/C++ est longue et pnible. (Les mmes remarques valent aussi dans
une large mesure pour le langage Java.)
Dautre part, la pratique moderne de ce langage fait abondamment appel des gnrateurs
dapplications et autres outils dassistance trs labors tels C++Builder, Kdevelop, etc. Ces
environnements de programmation peuvent certainement se rvler trs efficaces entre les mains de
programmeurs expriments, mais ils proposent demble beaucoup trop doutils complexes, et ils
prsupposent de la part de lutilisateur des connaissances quun dbutant ne matrise videmment pas
encore. Ce seront donc aux yeux de celui-ci de vritables usines gaz qui risquent de lui masquer
les mcanismes de base du langage lui-mme. Nous laisserons donc le C/C++ pour plus tard.
Pour nos dbuts dans ltude de la programmation, il nous semble prfrable dutiliser un langage de
plus haut niveau, moins contraignant, la syntaxe plus lisible. Aprs avoir successivement examin et
expriment quelque peu les langages Perl et Tcl/Tk , nous avons finalement dcid dadopter Python,
langage trs moderne la popularit grandissante.

Prsentation du langage Python


Ce texte de Stfane Fermigier date un peu, mais il reste dactualit pour lessentiel. Il est
extrait dun article paru dans le magazine Programmez! en dcembre 1998. Il est
galement disponible sur http://www.linux-center.org/articles/9812/python.html. Stfane
Fermigier est le co-fondateur de lAFUL (Association Francophone des Utilisateurs de
Linux et des logiciels libres).

Python est un langage portable, dynamique, extensible, gratuit, qui permet (sans limposer) une
approche modulaire et oriente objet de la programmation. Python est dvelopp depuis 1989 par
Guido van Rossum et de nombreux contributeurs bnvoles.
Caractristiques du langage
Dtaillons un peu les principales caractristiques de Python, plus prcisment, du langage et de ses
deux implantations actuelles :
1 Un

logiciel libre (Free Software) est avant tout un logiciel dont le code source est accessible tous (Open
Source). Souvent gratuit (ou presque), copiable et modifiable librement au gr de son acqureur, il est
gnralement le produit de la collaboration bnvole de centaines de dveloppeurs enthousiastes disperss
dans le monde entier. Son code source tant pluch par de trs nombreux spcialistes (tudiants et
professeurs universitaires), un logiciel libre se caractrise la plupart du temps par un trs haut niveau de
qualit technique. Le plus clbre des logiciels libres est le systme d exploitation GNU/Linux, dont la
popularit ne cesse de saccrotre de jour en jour.

VIII
Python est portable, non seulement sur les diffrentes variantes dUnix, mais aussi sur les OS
propritaires : Mac OS, BeOS, NeXTStep, MS-DOS et les diffrentes variantes de Windows. Un nouveau
compilateur, baptis JPython, est crit en Java et gnre du bytecode Java.
Python est gratuit, mais on peut lutiliser sans restriction dans des projets commerciaux.
Python convient aussi bien des scripts dune dizaine de lignes qu des projets complexes de
plusieurs dizaines de milliers de lignes.
La syntaxe de Python est trs simple et, combine des types de donnes volus (listes,
dictionnaires...), conduit des programmes la fois trs compacts et trs lisibles. fonctionnalits
gales, un programme Python (abondamment comment et prsent selon les canons standards)
est souvent de 3 5 fois plus court quun programme C ou C++ (ou mme Java) quivalent, ce qui
reprsente en gnral un temps de dveloppement de 5 10 fois plus court et une facilit de
maintenance largement accrue.
Python gre ses ressources (mmoire, descripteurs de fichiers...) sans intervention du
programmeur, par un mcanisme de comptage de rfrences (proche, mais diffrent, dun garbage collector).
Il ny a pas de pointeurs explicites en Python.
Python est (optionnellement) multi-thread.
Python est orient-objet. Il supporte lhritage multiple et la surcharge des oprateurs. Dans
son modle objets, et en reprenant la terminologie de C++, toutes les mthodes sont virtuelles.
Python intgre, comme Java ou les versions rcentes de C++, un systme dexceptions, qui
permettent de simplifier considrablement la gestion des erreurs.
Python est dynamique (linterprteur peut valuer des chanes de caractres reprsentant des
expressions ou des instructions Python), orthogonal (un petit nombre de concepts suffit
engendrer des constructions trs riches), rflectif (il supporte la mtaprogrammation, par
exemple la capacit pour un objet de se rajouter ou de senlever des attributs ou des mthodes, ou
mme de changer de classe en cours dexcution) et introspectif (un grand nombre doutils de
dveloppement, comme le debugger ou le profiler, sont implants en Python lui-mme).
Comme Scheme ou SmallTalk, Python est dynamiquement typ. Tout objet manipulable par le
programmeur possde un type bien dfini lexcution, qui na pas besoin dtre dclar
lavance.
Python possde actuellement deux implmentations. Lune, interprte, dans laquelle les
programmes Python sont compils en instructions portables, puis excuts par une machine
virtuelle (comme pour Java, avec une diffrence importante : Java tant statiquement typ, il est
beaucoup plus facile dacclrer lexcution dun programme Java que dun programme Python).
Lautre gnre directement du bytecode Java.
Python est extensible : comme Tcl ou Guile, on peut facilement linterfacer avec des bibliothques
C existantes. On peut aussi sen servir comme dun langage dextension pour des systmes logiciels
complexes.
La bibliothque standard de Python, et les paquetages contribus, donnent accs une grande
varit de services : chanes de caractres et expressions rgulires, services UNIX standards
(fichiers, pipes, signaux, sockets, threads...), protocoles Internet (Web, News, FTP, CGI, HTML...),
persistance et bases de donnes, interfaces graphiques.

IX
Python est un langage qui continue voluer, soutenu par une communaut dutilisateurs
enthousiastes et responsables, dont la plupart sont des supporters du logiciel libre. Paralllement
linterprteur principal, crit en C et maintenu par le crateur du langage, un deuxime
interprteur, crit en Java, est en cours de dveloppement.
Enfin, Python est un langage de choix pour traiter le XML.

Pour le professeur qui souhaite utiliser cet ouvrage comme support de cours
Nous souhaitons avec ces notes ouvrir un maximum de portes. notre niveau dtudes, il nous parat
important de montrer que la programmation dun ordinateur est un vaste univers de concepts et de
mthodes, dans lequel chacun peut trouver son domaine de prdilection. Nous ne pensons pas que
tous nos tudiants doivent apprendre exactement les mmes choses. Nous voudrions plutt quils
arrivent dvelopper chacun des comptences quelque peu diffrentes, qui leur permettent de se
valoriser leurs propres yeux ainsi qu ceux de leurs condisciples, et galement dapporter leur
contribution spcifique lorsquon leur proposera de collaborer des travaux denvergure.
De toute manire, notre proccupation primordiale doit tre darriver susciter lintrt, ce qui est
loin dtre acquis davance pour un sujet aussi ardu que la programmation dun ordinateur. Nous ne
voulons pas feindre de croire que nos jeunes lves vont se passionner demble pour la construction
de beaux algorithmes. Nous sommes plutt convaincus quun certain intrt ne pourra durablement
sinstaller qu partir du moment o ils commenceront raliser quils sont devenus capables de
dvelopper un projet personnel original, dans une certaine autonomie.
Ce sont ces considrations qui nous ont amens dvelopper une structure de cours que certains
trouveront peut-tre un peu chaotique. Nous commenons par une srie de chapitres trs courts, qui
expliquent sommairement ce quest lactivit de programmation et posent les quelques bases
indispensables la ralisation de petits programmes. Ceux-ci pourront faire appel trs tt des
bibliothques dobjets existants, tels ceux de linterface graphique tkinter par exemple, afin que ce
concept dobjet devienne rapidement familier. Ils devront tre suffisamment attrayants pour que leurs
auteurs aient le sentiment davoir dj acquis une certaine matrise. Nous souhaiterions en effet que
les lves puissent dj raliser une petite application graphique ds la fin de leur premire anne
dtudes.
Trs concrtement, cela signifie que nous pensons pouvoir explorer les huit premiers chapitres de ces
notes durant la premire anne de cours. Cela suppose que lon aborde dabord toute une srie de
concepts importants (types de donnes, variables, instructions de contrle du flux, fonctions et
boucles) dune manire assez rapide, sans trop se proccuper de ce que chaque concept soit
parfaitement compris avant de passer au suivant, en essayant plutt dinculquer le got de la
recherche personnelle et de lexprimentation. Il sera souvent plus efficace de rexpliquer les notions
et les mcanismes essentiels plus tard, en situation et dans des contextes varis.
Dans notre esprit, cest surtout en seconde anne que lon cherchera structurer les connaissances
acquises, en les approfondissant. Les algorithmes seront davantage dcortiqus et comments. Les
projets, cahiers des charges et mthodes danalyse seront discuts en concertation. On exigera la
tenue rgulire dun cahier de notes et la rdaction de rapports techniques pour certains travaux.

X
Lobjectif ultime sera pour chaque lve de raliser un projet de programmation original dune
certaine importance. On sefforcera donc de boucler ltude thorique des concepts essentiels
suffisamment tt dans lanne scolaire, afin que chacun puisse disposer du temps ncessaire.
Il faut bien comprendre que les nombreuses informations fournies dans ces notes concernant une
srie de domaines particuliers (gestion des interfaces graphiques, des communications, des bases de
donnes, etc.) sont facultatives. Ce sont seulement une srie de suggestions et de repres que nous
avons inclus pour aider les tudiants choisir et commencer leur projet personnel de fin dtudes.
Nous ne cherchons en aucune manire former des spcialistes dun certain langage ou dun certain
domaine technique : nous voulons simplement donner un petit aperu des immenses possibilits qui
soffrent celui qui se donne la peine dacqurir une comptence de programmeur.

Versions du langage
Python continue voluer, mais cette volution ne vise qu amliorer ou perfectionner le produit. Il
est donc trs rare quil faille modifier les programmes afin de les adapter une nouvelle version qui
serait devenue incompatible avec les prcdentes. Les exemples de ce livre ont t raliss les uns
aprs les autres sur une priode de temps relativement longue : certains ont t dvelopps sous
Python 1.5.2, puis dautres sous Python 1.6, Python 2.0, 2.1, 2.2, 2.3, 2.4, etc. Ils nont gure ncessit de
modifications avant lapparition de Python 3.
Cette nouvelle version du langage a cependant apport quelques changements de fond qui lui
confrent une plus grande cohrence et mme une plus grande facilit dutilisation, mais qui imposent
une petite mise jour de tous les scripts crits pour les versions prcdentes. La prsente dition de ce
livre a donc t remanie, non seulement pour adapter ses exemples la nouvelle version, mais
surtout pour tirer parti de ses amliorations, qui en font probablement le meilleur outil
dapprentissage de la programmation lheure actuelle.
Installez donc sur votre systme la dernire version disponible (quelques-uns de nos exemples
ncessitent dsormais la version 3.1 ou une version postrieure), et amusez-vous bien ! Si toutefois
vous devez analyser des scripts dvelopps pour une version antrieure, sachez que des outils de
conversion existent (voir en particulier le script 2to3.py), et que nous maintenons en ligne sur notre
site web http://inforef.be/swi/python.htm la prcdente mouture de ce texte, adapte aux versions
antrieures de Python, et toujours librement tlchargeable.

Distribution de Python et bibliographie


Les diffrentes versions de Python (pour Windows, Unix, etc.), son tutoriel original, son manuel de
rfrence, la documentation des bibliothques de fonctions, etc. sont disponibles en tlchargement
gratuit depuis Internet, partir du site web officiel : http://www.python.org
Vous pouvez aussi trouver en ligne et en franais, lexcellent cours sur Python 3 de Robert Cordeau,
professeur lIUT dOrsay, qui complte excellemment celui-ci. Il est disponible sur le site de lAFPY,
ladresse : http://www.afpy.org/Members/bcordeau/Python3v1-1.pdf/download

XI
Il existe galement de trs bons ouvrages imprims concernant Python. La plupart concernent encore
Python 2.x, mais vous ne devrez gure prouver de difficults adapter leurs exemples Python 3. En
langue franaise, vous pourrez trs profitablement consulter les manuels ci-aprs :

Programmation Python, par Tarek Ziad, ditions Eyrolles, Paris, 2009, 586 p., ISBN
978-2-212-12483-5. Cest lun des premiers ouvrages dits directement en langue franaise sur le
langage Python. Excellent. Une mine de renseignements essentielle si vous voulez acqurir les
meilleures pratiques et vous dmarquer des dbutants.
Au cur de Python, volumes 1 et 2, par Wesley J. Chun, traduction de Python core programming, 2d
edition (Prentice Hall) par Marie-Ccile Baland, Anne Bohy et Luc Carit, ditions CampusPress,
Paris, 2007, respectivement 645 et 385 p., ISBN 978-2-7440-2148-0 et 978-2-7440-2195-4. Cest un
ouvrage de rfrence indispensable, trs bien crit.
Dautres excellents ouvrages en franais taient proposs par la succursale franaise de la maison
dditions OReilly, laquelle a malheureusement disparu. En langue anglaise, le choix est videmment
beaucoup plus vaste. Nous apprcions personnellement beaucoup Python : How to program, par Deitel,
Liperi & Wiedermann, Prentice Hall, Upper Saddle River - NJ 07458, 2002, 1300 p., ISBN 0-13-092361-3,
trs complet, trs clair, agrable lire et qui utilise une mthodologie prouve.
Pour aller plus loin, notamment dans lutilisation de la bibliothque graphique Tkinter, on pourra
utilement consulter Python and Tkinter Programming, par John E. Grayson, Manning publications co.,
Greenwich (USA), 2000, 658 p., ISBN 1-884777-81-3, et surtout lincontournable Programming Python
(second edition) de Mark Lutz, ditions OReilly, 2001, 1255 p., ISBN 0-596-00085-5, qui est une
extraordinaire mine de renseignements sur de multiples aspects de la programmation moderne (sur
tous systmes).
Si vous savez dj bien programmer, et que vous souhaitez progresser encore en utilisant les concepts
les plus avancs de lalgorithmique Pythonienne, procurez-vous Python cookbook, par Alex Martelli et
David Ascher, ditions OReilly, 2002, 575 p., ISBN 0-596-00167-3, dont les recettes sont savoureuses.

Exemples du livre
Le code source des exemples de ce livre peut tre tlcharg partir du site de lauteur :
http://inforef.be/swi/python.htm

ou encore cette adresse :


http://infos.pythomium.net/download/cours_python.zip

ainsi que sur la fiche de louvrage :


http://www.editions-eyrolles.com

XII

Remerciements
Ce livre est pour une partie le rsultat dun travail personnel, mais pour une autre bien plus
importante la compilation dinformations et dides mises la disposition de tous par des
professeurs et des chercheurs bnvoles.
La source qui a inspir mes premires bauches du livre est le cours de A.Downey, J.Elkner &
C.Meyers : How to think like a computer scientist (http://greenteapress.com/thinkpython/thinkCSpy ).
Merci encore ces professeurs enthousiastes. Javoue aussi mtre inspir du tutoriel original crit par
Guido van Rossum lui-mme (lauteur principal de Python), ainsi que dexemples et de documents
divers manant de la (trs active) communaut des utilisateurs de Python. Il ne mest
malheureusement pas possible de prciser davantage les rfrences de tous ces textes, mais je
voudrais que leurs auteurs soient assurs de toute ma reconnaissance.
Merci galement tous ceux qui uvrent au dveloppement de Python, de ses accessoires et de sa
documentation, commencer par Guido van Rossum, bien sr, mais sans oublier non plus tous les
autres ((mal)heureusement trop nombreux pour que je puisse les citer tous ici).
Merci encore mes collgues Freddy Klich et David Carrera, professeurs lInstitut Saint-Jean
Berchmans de Lige, qui ont accept de se lancer dans laventure de ce nouveau cours avec leurs
lves, et ont galement suggr de nombreuses amliorations. Un merci tout particulier Christophe
Morvan, professeur lIUT de Marne-la-Valle, pour ses avis prcieux et ses encouragements, et
Robert Cordeau, professeur lIUT dOrsay, pour ses conseils et sa courageuse relecture. Grand merci
aussi Florence Leroy, mon ditrice chez OReilly, qui a corrig mes incohrences et mes belgicismes
avec une comptence sans faille. Merci encore mes partenaires actuels chez Eyrolles, Muriel Shan Sei
Fan, Ta-Marc Le Thanh, Anne-Lise Banath et Igor Barzilai qui ont efficacement pris en charge cette
nouvelle dition.
Merci enfin mon pouse Suzel, pour sa patience et sa comprhension.

Table des matires


1. lcole des sorciers .................................................................................................. 1
Botes noires et pense magique............................................................................................................................. 1
Magie blanche, magie noire.................................................................................................................................... 3
La dmarche du programmeur.............................................................................................................................. 3
Langage machine, langage de programmation................................................................................................... 5
dition du code source Interprtation .............................................................................................................. 6
Mise au point dun programme Recherche des erreurs (debug).................................................................... 6

Erreurs de syntaxe .....................................................................................................................................................................................................7


Erreurs smantiques .................................................................................................................................................................................................7
Erreurs lexcution .................................................................................................................................................................................................8

Recherche des erreurs et exprimentation........................................................................................................... 8

2. Premiers pas .............................................................................................................. 11


Calculer avec Python.............................................................................................................................................. 11
Donnes et variables.............................................................................................................................................. 13
Noms de variables et mots rservs..................................................................................................................... 14
Affectation (ou assignation)................................................................................................................................. 14
Afficher la valeur dune variable......................................................................................................................... 15
Typage des variables.............................................................................................................................................. 16
Affectations multiples............................................................................................................................................ 17
Oprateurs et expressions..................................................................................................................................... 17
Priorit des oprations........................................................................................................................................... 18
Composition............................................................................................................................................................. 19

3. Contrle du flux dexcution ................................................................................... 21


Squence dinstructions......................................................................................................................................... 21
Slection ou excution conditionnelle................................................................................................................. 22
Oprateurs de comparaison.................................................................................................................................. 23
Instructions composes blocs dinstructions................................................................................................... 23
Instructions imbriques........................................................................................................................................ 24
Quelques rgles de syntaxe Python...................................................................................................................... 24

Les limites des instructions et des blocs sont dfinies par la mise en page ............................................................................................... 25
Instruction compose : en-tte, double point, bloc dinstructions indent ................................................................................................25
Les espaces et les commentaires sont normalement ignors .........................................................................................................................26

XIV

4. Instructions rptitives ............................................................................................ 27


Raffectation........................................................................................................................................................... 27
Rptitions en boucle Linstruction while........................................................................................................ 28

Commentaires .......................................................................................................................................................................................................... 28
Remarques .................................................................................................................................................................................................................29
laboration de tables ..............................................................................................................................................................................................29
Construction dune suite mathmatique ........................................................................................................................................................... 30

Premiers scripts, ou comment conserver nos programmes............................................................................. 31


Problmes ventuels lis aux caractres accentus .........................................................................................................................................34

5. Principaux types de donnes ................................................................................... 37


Les donnes numriques........................................................................................................................................ 37

Le type integer ..........................................................................................................................................................................................................37

Le type float............................................................................................................................................................. 39
Les donnes alphanumriques.............................................................................................................................. 40
Le type string ............................................................................................................................................................................................................41
Remarques.......................................................................................................................................................................................................... 42
Triple quotes....................................................................................................................................................................................................... 42
Accs aux caractres individuels dune chane ................................................................................................................................................42
Oprations lmentaires sur les chanes ............................................................................................................................................................43

Les listes (premire approche).............................................................................................................................. 44

6. Fonctions prdfinies ............................................................................................... 49


La fonction print().................................................................................................................................................. 49
Interaction avec lutilisateur : la fonction input().............................................................................................50
Importer un module de fonctions......................................................................................................................... 50
Un peu de dtente avec le module turtle............................................................................................................. 52
Vracit/fausset dune expression..................................................................................................................... 53
Rvision.................................................................................................................................................................... 55

Contrle du flux utilisation dune liste simple ...............................................................................................................................................55


Boucle while instructions imbriques ..............................................................................................................................................................56

7. Fonctions originales ................................................................................................. 61


Dfinir une fonction............................................................................................................................................... 61

Fonction simple sans paramtres ........................................................................................................................................................................ 62


Fonction avec paramtre .......................................................................................................................................................................................63
Utilisation dune variable comme argument ....................................................................................................................................................64
Remarque importante...................................................................................................................................................................................... 64
Fonction avec plusieurs paramtres ...................................................................................................................................................................65
Notes.................................................................................................................................................................................................................... 65

Variables locales, variables globales................................................................................................................... 66


Vraies fonctions et procdures............................................................................................................................. 68
Notes ...........................................................................................................................................................................................................................69

Utilisation des fonctions dans un script............................................................................................................. 70

Notes ...........................................................................................................................................................................................................................70

Modules de fonctions.............................................................................................................................................. 71
Typage des paramtres.......................................................................................................................................... 76

XV
Valeurs par dfaut pour les paramtres............................................................................................................. 76
Arguments avec tiquettes.................................................................................................................................... 77

8. Utilisation de fentres et de graphismes ............................................................... 79


Interfaces graphiques (GUI).................................................................................................................................. 79
Premiers pas avec tkinter..................................................................................................................................... 80

Examinons prsent plus en dtail chacune des lignes de commandes excutes ................................................................................. 80

Programmes pilots par des vnements............................................................................................................ 83

Exemple graphique : trac de lignes dans un canevas ....................................................................................................................................85


Exemple graphique : deux dessins alterns .......................................................................................................................................................88
Exemple graphique : calculatrice minimaliste ................................................................................................................................................. 90
Exemple graphique : dtection et positionnement dun clic de souris ........................................................................................................92

Les classes de widgets tkinter............................................................................................................................... 93


Utilisation de la mthode grid pour contrler la disposition des widgets..................................................... 95
Composition dinstructions pour crire un code plus compact....................................................................... 98
Modification des proprits dun objet Animation....................................................................................... 100
Animation automatique Rcursivit.............................................................................................................. 103

9. Manipuler des fichiers ........................................................................................... 107


Utilit des fichiers................................................................................................................................................. 107
Travailler avec des fichiers................................................................................................................................. 108
Noms de fichiers le rpertoire courant........................................................................................................... 109
Les deux formes dimportation........................................................................................................................... 110
criture squentielle dans un fichier................................................................................................................. 111

Notes .........................................................................................................................................................................................................................111

Lecture squentielle dun fichier........................................................................................................................ 112

Notes .........................................................................................................................................................................................................................112

Linstruction break pour sortir dune boucle................................................................................................... 113


Fichiers texte......................................................................................................................................................... 114

Remarques ...............................................................................................................................................................................................................115

Enregistrement et restitution de variables diverses....................................................................................... 116


Gestion des exceptions : les instructions try except else........................................................................... 117

10. Approfondir les structures de donnes ................................................................ 121


Le point sur les chanes de caractres............................................................................................................... 121

Indiage, extraction, longueur ........................................................................................................................................................................... 121


Extraction de fragments de chanes ..................................................................................................................................................................122
Concatnation, rptition ....................................................................................................................................................................................123
Parcours dune squence : linstruction for ... in ... ........................................................................................................................................ 124
Appartenance dun lment une squence : linstruction in utilise seule ...........................................................................................125
Les chanes sont des squences non modifiables ........................................................................................................................................... 126
Les chanes sont comparables ............................................................................................................................................................................ 127
La norme Unicode ................................................................................................................................................................................................. 127
Squences doctets : le type bytes .......................................................................................................................................................................129
Lencodage Utf-8 ....................................................................................................................................................................................................131
Conversion (encodage/dcodage) des chanes ............................................................................................................................................... 132
Conversion dune chane bytes en chane string...................................................................................................................................... 132

XVI
Conversion dune chane string en chane bytes...................................................................................................................................... 133
Conversions automatiques lors du traitement des fichiers.................................................................................................................... 133
Cas des scripts Python.................................................................................................................................................................................... 134
Accder dautres caractres que ceux du clavier ........................................................................................................................................135
Les chanes sont des objets ..................................................................................................................................................................................136
Fonctions intgres ............................................................................................................................................................................................... 138
Formatage des chanes de caractres ............................................................................................................................................................... 138
Formatage des chanes lancienne ........................................................................................................................................................... 140

Le point sur les listes............................................................................................................................................ 141


Dfinition dune liste accs ses lments ................................................................................................................................................... 141
Les listes sont modifiables ...................................................................................................................................................................................142
Les listes sont des objets .......................................................................................................................................................................................142
Techniques de slicing avanc pour modifier une liste ..................................................................................................................................144
Insertion dun ou plusieurs lments nimporte o dans une liste....................................................................................................... 144
Suppression / remplacement dlments................................................................................................................................................... 144
Cration dune liste de nombres laide de la fonction range() .................................................................................................................145
Parcours dune liste laide de for, range() et len() ...................................................................................................................................... 145
Une consquence importante du typage dynamique ................................................................................................................................... 146
Oprations sur les listes ....................................................................................................................................................................................... 146
Test dappartenance .............................................................................................................................................................................................147
Copie dune liste .....................................................................................................................................................................................................147
Petite remarque concernant la syntaxe..................................................................................................................................................... 148
Nombres alatoires histogrammes .................................................................................................................................................................149
Tirage au hasard de nombres entiers ...............................................................................................................................................................151

Les tuples............................................................................................................................................................... 152

Oprations sur les tuples ......................................................................................................................................................................................153

Les dictionnaires................................................................................................................................................... 153


Cration dun dictionnaire ..................................................................................................................................................................................154
Oprations sur les dictionnaires ........................................................................................................................................................................ 154
Test dappartenance .............................................................................................................................................................................................155
Les dictionnaires sont des objets ....................................................................................................................................................................... 155
Parcours dun dictionnaire ................................................................................................................................................................................. 156
Les cls ne sont pas ncessairement des chanes de caractres ..................................................................................................................157
Les dictionnaires ne sont pas des squences ...................................................................................................................................................158
Construction dun histogramme laide dun dictionnaire ........................................................................................................................159
Contrle du flux dexcution laide dun dictionnaire ...............................................................................................................................160

11. Classes, objets, attributs ........................................................................................ 163


Utilit des classes.................................................................................................................................................. 163
Dfinition dune classe lmentaire................................................................................................................... 164
Attributs (ou variables) dinstance.................................................................................................................... 166
Passage dobjets comme arguments dans lappel dune fonction..................................................................167
Similitude et unicit............................................................................................................................................. 167
Objets composs dobjets..................................................................................................................................... 169
Objets comme valeurs de retour dune fonction.............................................................................................. 170
Modification des objets........................................................................................................................................ 170

12. Classes, mthodes, hritage ................................................................................... 173


Dfinition dune mthode.................................................................................................................................... 174

XVII
Dfinition concrte dune mthode dans un script ........................................................................................................................................175
Essai de la mthode, dans une instance quelconque .....................................................................................................................................175

La mthode constructeur.................................................................................................................................... 176


Exemple ....................................................................................................................................................................................................................176

Espaces de noms des classes et instances.......................................................................................................... 180


Hritage.................................................................................................................................................................. 181
Hritage et polymorphisme................................................................................................................................. 182

Commentaires ........................................................................................................................................................................................................ 184

Modules contenant des bibliothques de classes............................................................................................. 187

13. Classes et interfaces graphiques ........................................................................... 191


Code des couleurs : un petit projet bien encapsul.......................................................................................... 191

Cahier des charges de notre programme.................................................................................................................................................... 192


Mise en uvre concrte................................................................................................................................................................................. 192
Commentaires.................................................................................................................................................................................................. 193

Petit train : hritage, change dinformations entre objets........................................................................... 195


Cahier des charges.......................................................................................................................................................................................... 196
Implmentation............................................................................................................................................................................................... 196
Commentaires.................................................................................................................................................................................................. 197

OscilloGraphe : un widget personnalis............................................................................................................ 198

Exprimentation.............................................................................................................................................................................................. 200
Cahier des charges.......................................................................................................................................................................................... 201
Implmentation............................................................................................................................................................................................... 201

Curseurs : un widget composite.......................................................................................................................... 203

Prsentation du widget Scale ............................................................................................................................................................................. 203


Construction dun panneau de contrle trois curseurs .............................................................................................................................204
Commentaires.................................................................................................................................................................................................. 206
Propagation des vnements........................................................................................................................................................................ 208

Intgration de widgets composites dans une application synthse.............................................................. 208

Commentaires.................................................................................................................................................................................................. 210

14. Et pour quelques widgets de plus... ....................................................................... 217


Les boutons radio.................................................................................................................................................. 217

Commentaires ........................................................................................................................................................................................................ 218

Utilisation de cadres pour la composition dune fentre................................................................................ 219

Commentaires ........................................................................................................................................................................................................ 220

Comment dplacer des dessins laide de la souris........................................................................................ 221


Commentaires ........................................................................................................................................................................................................ 223

Widgets complmentaires, widgets composites............................................................................................... 225

Combo box simplifi ..............................................................................................................................................................................................225


Commentaires.................................................................................................................................................................................................. 227
Le widget Text assorti dun ascenseur ..............................................................................................................................................................228
Gestion du texte affich.................................................................................................................................................................................. 229
Commentaires.................................................................................................................................................................................................. 230
Canevas avec barres de dfilement ................................................................................................................................................................... 232
Commentaires.................................................................................................................................................................................................. 234

Application fentres multiples paramtrage implicite.............................................................................235

Commentaires ........................................................................................................................................................................................................ 238

XVIII
Barres doutils expressions lambda................................................................................................................ 239
Mtaprogrammation expressions lambda ................................................................................................................................................... 240
Passage dune fonction (ou dune mthode) comme argument ..................................................................................................................241

Fentres avec menus............................................................................................................................................ 242

Cahier des charges de lexercice .........................................................................................................................................................................243


Premire bauche du programme ..................................................................................................................................................................... 243
Analyse du script............................................................................................................................................................................................. 244
Ajout de la rubrique Musiciens ..........................................................................................................................................................................246
Analyse du script............................................................................................................................................................................................. 247
Ajout de la rubrique Peintres ..............................................................................................................................................................................247
Analyse du script............................................................................................................................................................................................. 248
Ajout de la rubrique Options .............................................................................................................................................................................. 248
Menu avec cases cocher ....................................................................................................................................................................................249
Menu avec choix exclusifs ................................................................................................................................................................................... 250
Contrle du flux dexcution laide dune liste ............................................................................................................................................ 251
Prslection dune rubrique ................................................................................................................................................................................252

15. Analyse de programmes concrets ......................................................................... 255


Jeu des bombardes................................................................................................................................................ 255

Prototypage dune classe Canon ........................................................................................................................................................................ 258


Commentaires.................................................................................................................................................................................................. 260
Ajout de mthodes au prototype ........................................................................................................................................................................261
Commentaires.................................................................................................................................................................................................. 262
Dveloppement de lapplication .........................................................................................................................................................................263
Commentaires.................................................................................................................................................................................................. 268
Dveloppements complmentaires ....................................................................................................................................................................269
Commentaires.................................................................................................................................................................................................. 273

Jeu de Ping............................................................................................................................................................. 273

Principe ....................................................................................................................................................................................................................274
Programmation ......................................................................................................................................................................................................274
Cahier des charges du logiciel dvelopper.............................................................................................................................................. 275

16. Gestion dune base de donnes .............................................................................. 279


Les bases de donnes............................................................................................................................................ 279

SGBDR Le modle client/serveur .....................................................................................................................................................................280


Le langage SQL .......................................................................................................................................................................................................281
SQLite ....................................................................................................................................................................................................................... 282
Cration de la base de donnes Objets connexion et curseur ...................................................................................................... 282
Connexion une base de donnes existante ................................................................................................................................................... 284
Recherches slectives dans une base de donnes ...........................................................................................................................................286
La requte select .................................................................................................................................................................................................... 288

bauche dun logiciel client pour PostgreSQL.................................................................................................. 289

Dcrire la base de donnes dans un dictionnaire dapplication .................................................................................................................290


Dfinir une classe dobjets-interfaces ...............................................................................................................................................................293
Commentaires.................................................................................................................................................................................................. 294
Construire un gnrateur de formulaires ........................................................................................................................................................296
Commentaires.................................................................................................................................................................................................. 296
Le corps de lapplication ...................................................................................................................................................................................... 297
Commentaires.................................................................................................................................................................................................. 299

XIX

17. Applications web ..................................................................................................... 301


Pages web interactives......................................................................................................................................... 301
Un serveur web en pur Python !......................................................................................................................... 303

Premire bauche : mise en ligne dune page web minimaliste ..................................................................................................................304


Ajout dune deuxime page .................................................................................................................................................................................307
Prsentation et traitement dun formulaire ....................................................................................................................................................308
Analyse de la communication et des erreurs ..................................................................................................................................................309
Structuration dun site pages multiples ....................................................................................................................................................... 311
Prise en charge des sessions ................................................................................................................................................................................313

Ralisation concrte dun site web interactif................................................................................................... 314


Le script ................................................................................................................................................................................................................... 316
Les patrons HTML ...........................................................................................................................................................................................325

Autres dveloppements....................................................................................................................................... 328

18. Imprimer avec Python ........................................................................................... 329


Linterface graphique peut aider....................................................................................................................... 330
Le PDF, langage de description de page pour limpression............................................................................ 331
Installer Python 2.6 ou 2.7 pour utiliser des modules Python 2.....................................................................332
Exploitation de la bibliothque ReportLab....................................................................................................... 336

Un premier document PDF rudimentaire ........................................................................................................................................................336


Commentaires.................................................................................................................................................................................................. 336
Gnrer un document plus labor ................................................................................................................................................................... 338
Commentaires.................................................................................................................................................................................................. 340

Documents de plusieurs pages et gestion des paragraphes........................................................................... 342

Exemple de script pour la mise en page dun fichier texte .......................................................................................................................... 343
Commentaires ........................................................................................................................................................................................................ 344
En conclusion................................................................................................................................................................................................... 347

19. Communications travers un rseau et multithreading ................................... 351


Les sockets............................................................................................................................................................. 351

Construction dun serveur rudimentaire .........................................................................................................................................................352


Commentaires.................................................................................................................................................................................................. 353
Construction dun client rudimentaire ............................................................................................................................................................ 354
Commentaires.................................................................................................................................................................................................. 355

Gestion de plusieurs tches en parallle laide de threads.......................................................................... 355

Client rseau grant lmission et la rception simultanes ....................................................................................................................... 356


Commentaires.................................................................................................................................................................................................. 358
Serveur rseau grant les connexions de plusieurs clients en parallle ................................................................................................... 359
Commentaires.................................................................................................................................................................................................. 360

Jeu des bombardes, version rseau.................................................................................................................... 361

Programme serveur : vue densemble ...............................................................................................................................................................362


Protocole de communication .............................................................................................................................................................................. 363
Remarques complmentaires....................................................................................................................................................................... 364
Programme serveur : premire partie ...............................................................................................................................................................365
Synchronisation de threads concurrents laide de verrous (thread locks) ...........................................................................................368
Utilisation......................................................................................................................................................................................................... 368
Programme serveur : suite et fin ........................................................................................................................................................................369
Commentaires.................................................................................................................................................................................................. 371
Programme client ..................................................................................................................................................................................................372

XX
Commentaires.................................................................................................................................................................................................. 375
Conclusions et perspectives .......................................................................................................................................................................... 375

Utilisation de threads pour optimiser les animations.................................................................................... 376

Temporisation des animations laide de after() .........................................................................................................................................376


Temporisation des animations laide de time.sleep() ................................................................................................................................377
Exemple concret .....................................................................................................................................................................................................378
Commentaires.................................................................................................................................................................................................. 379

20. Installation de Python ............................................................................................ 381


Sous Windows....................................................................................................................................................... 381
Sous Linux............................................................................................................................................................. 381
Sous Mac OS.......................................................................................................................................................... 381
Installation de Cherrypy...................................................................................................................................... 382
Installation de pg8000.......................................................................................................................................... 382
Installation de ReportLab et de Python Imaging Library.............................................................................. 383

21. Solutions des exercices ........................................................................................... 385


22. Licence associe cet ouvrage .............................................................................. 445
23. Index ........................................................................................................................ 451

1
lcole des sorciers

Apprendre programmer est une activit dj trs intressante en elle-mme : elle peut stimuler
puissamment votre curiosit intellectuelle. Mais ce nest pas tout. Acqurir cette comptence vous
ouvre galement la voie menant la ralisation de projets tout fait concrets (utiles ou ludiques),
ce qui vous procurera certainement beaucoup de fiert et de grandes satisfactions.
Avant de nous lancer dans le vif du sujet, nous allons vous proposer ici quelques rflexions sur la
nature de la programmation et le comportement parfois trange de ceux qui la pratiquent, ainsi
que lexplication de quelques concepts fondamentaux. Il nest pas vraiment difficile dapprendre
programmer, mais il faut de la mthode et une bonne dose de persvrance, car vous pourrez
continuer progresser sans cesse dans cette science : elle na aucune limite.

Botes noires et pense magique


Une caractristique remarquable de notre socit moderne est que nous vivons de plus en plus
entours de botes noires. Les scientifiques ont lhabitude de nommer ainsi les divers dispositifs
technologiques que nous utilisons couramment, sans en connatre ni la structure ni le fonctionnement
exacts. Tout le monde sait se servir dun tlphone, par exemple, alors quil nexiste quun trs petit
nombre de techniciens hautement spcialiss capables den concevoir un nouveau modle.
Des botes noires existent dans tous les domaines, et pour tout le monde. En gnral, cela ne nous
affecte gure, car nous pouvons nous contenter dune comprhension sommaire de leur mcanisme
pour les utiliser sans tats dme. Dans la vie courante, par exemple, la composition prcise dune pile
lectrique ne nous importe gure. Le simple fait de savoir quelle produit son lectricit partir dune
raction chimique nous suffit pour admettre sans difficult quelle sera puise aprs quelque temps
dutilisation, et quelle sera alors devenue un objet polluant quil ne faudra pas jeter nimporte o.
Inutile donc den savoir davantage.
Il arrive cependant que certaines botes noires deviennent tellement complexes que nous narrivons
plus en avoir une comprhension suffisante pour les utiliser tout--fait correctement dans nimporte
quelle circonstance. Nous pouvons alors tre tents de tenir leur encontre des raisonnements qui se
rattachent la pense magique, cest--dire une forme de pense faisant appel lintervention de
proprits ou de pouvoirs surnaturels pour expliquer ce que notre raison narrive pas comprendre.

lcole des sorciers

Cest ce qui se passe lorsquun magicien nous montre un tour de passe-passe, et que nous sommes
enclins croire quil possde un pouvoir particulier, tel un don de double vue , ou accepter
lexistence de mcanismes paranormaux ( fluide magntique , etc.), tant que nous navons pas
compris le truc utilis.
Du fait de leur extraordinaire complexit, les ordinateurs constituent bien videmment lexemple type
de la bote noire. Mme si vous avez limpression davoir toujours vcu entour de moniteurs vido et
de claviers, il est fort probable que vous nayez quune ide trs vague de ce qui se passe rellement
dans la machine, par exemple lorsque vous dplacez la souris, et quen consquence de ce geste un
petit dessin en forme de flche se dplace docilement sur votre cran. Quest-ce qui se dplace, au
juste ? Vous sentez-vous capable de lexpliquer en dtail, sans oublier (entre autres) les capteurs, les
ports dinterface, les mmoires, les portes et bascules logiques, les transistors, les bits, les octets, les
interruptions processeur, les cristaux liquides de lcran, la micro-programmation, les pixels, le
codage des couleurs... ?
De nos jours, plus personne ne peut prtendre matriser absolument toutes les connaissances
techniques et scientifiques mises en uvre dans le fonctionnement dun ordinateur. Lorsque nous
utilisons ces machines, nous sommes donc forcment amens les traiter mentalement, en partie tout
au moins, comme des objets magiques, sur lesquels nous sommes habilits exercer un certain
pouvoir, magique lui aussi.
Par exemple, nous comprenons tous trs bien une instruction telle que dplacer la fentre
dapplication en la saisissant par sa barre de titre . Dans le monde rel, nous savons parfaitement ce
quil faut faire pour lexcuter, savoir manipuler un dispositif technique familier (souris, pav
tactile) qui va transmettre des impulsions lectriques travers une machinerie dune complexit
prodigieuse, avec pour effet ultime la modification de ltat de transparence ou de luminosit dune
partie des pixels de lcran. Mais dans notre esprit, il ne sera nullement question dinteractions
physiques ni de circuiterie complexe. Cest un objet tout fait virtuel qui sera activ (la flche du
curseur se dplaant lcran), et qui agira comme une baguette magique, pour faire obir un objet
tout aussi virtuel et magique (la fentre dapplication). Lexplication rationnelle de ce qui se passe
effectivement dans la machine est donc escamote au profit dun raisonnement figur, qui nous
rassure par sa simplicit, mais qui est bel et bien une illusion.
Si vous vous intressez la programmation, sachez que vous serez constamment confront diverses
formes de cette pense magique , non seulement chez les autres (par exemple ceux qui vous
demanderont de raliser tel ou tel programme), mais aussi et surtout dans vos propres reprsentations
mentales. Vous devrez inlassablement dmonter ces pseudo-raisonnements qui ne sont en fait que des
spculations, bases sur des interprtations figuratives simplifies de la ralit, pour arriver mettre
en lumire (au moins en partie) leurs implications concrtes vritables.
Ce qui est un peu paradoxal, et qui justifie le titre de ce chapitre, cest quen progressant dans cette
comptence, vous allez acqurir de plus en plus de pouvoir sur la machine, et de ce fait vous allez
vous-mme devenir petit petit aux yeux des autres, une sorte de magicien !
Bienvenue donc, comme le clbre Harry Potter, lcole des sorciers !

Magie blanche, magie noire

Magie blanche, magie noire


Nous navons bien videmment aucune intention dassimiler la programmation une science occulte.
Si nous vous accueillons ici comme un apprenti sorcier, cest seulement pour attirer votre attention
sur ce quimplique cette image que vous donnerez probablement de vous-mme (involontairement)
vos contemporains. Il peut tre intressant demprunter quelques termes au vocabulaire de la magie
pour illustrer plaisamment certaines pratiques.
La programmation est lart dapprendre une machine comment accomplir de nouvelles tches,
quelle navait jamais t capable deffectuer auparavant. Cest par la programmation que vous pourrez
acqurir le plus de contrle, non seulement sur votre machine, mais aussi peut-tre sur celles des
autres par lintermdiaire des rseaux. Dune certaine faon, cette activit peut donc tre assimile
une forme particulire de magie. Elle donne effectivement celui qui lexerce un certain pouvoir,
mystrieux pour le plus grand nombre, voire inquitant quand on se rend compte quil peut tre
utilis des fins malhonntes.
Dans le monde de la programmation, on dsigne par le terme hacker les programmeurs chevronns qui
ont perfectionn les systmes dexploitation de type Unix et mis au point les techniques de
communication qui sont la base du dveloppement extraordinaire de lInternet. Ce sont eux
galement qui continuent inlassablement produire et amliorer les logiciels libres (Open Source).
Selon notre analogie, les hackers sont donc des matres-sorciers, qui pratiquent la magie blanche.
Mais il existe aussi un autre groupe de gens que les journalistes mal informs dsignent erronment
sous le nom de hackers, alors quils devraient plutt les appeler crackers. Ces personnes se prtendent
hackers parce quils veulent faire croire quils sont trs comptents, alors quen gnral ils ne le sont
gure. Ils sont cependant trs nuisibles, parce quils utilisent leurs quelques connaissances pour
rechercher les moindres failles des systmes informatiques construits par dautres, afin dy effectuer
toutes sortes doprations illicites : vol dinformations confidentielles, escroquerie, diffusion de spam,
de virus, de propagande haineuse, de pornographie et de contrefaons, destruction de sites web, etc.
Ces sorciers dpravs sadonnent bien sr une forme grave de magie noire.
Mais il y en a une autre. Les vrais hackers cherchent promouvoir dans leur domaine une certaine
thique, base principalement sur lmulation et le partage des connaissances 2. La plupart dentre eux
sont des perfectionnistes, qui veillent non seulement ce que leurs constructions logiques soient
efficaces, mais aussi ce quelles soient lgantes, avec une structure parfaitement lisible et
documente. Vous dcouvrirez rapidement quil est ais de produire la va-vite des programmes qui
fonctionnent, certes, mais qui sont obscurs et confus, indchiffrables pour toute autre personne que
leur auteur (et encore !). Cette forme de programmation absconse et ingrable est souvent aussi
qualifie de magie noire par les hackers.

La dmarche du programmeur
Comme le sorcier, le programmeur comptent semble dot dun pouvoir trange qui lui permet de
transformer une machine en une autre, une machine calculer en une machine crire ou dessiner,
2 Voir

ce sujet le texte de Eric Steven Raymond : Comment devenir un hacker , reproduit sur de
nombreux sites, notamment sur : http://www.secuser.com/dossiers/devenir_hacker.htm , ou encore sur :
http://www.forumdz.com/showthread.php?t=4593

lcole des sorciers

par exemple, un peu la manire dun sorcier qui transformerait un prince charmant en grenouille,
laide de quelques incantations mystrieuses entres au clavier. Comme le sorcier, il est capable de
gurir une application apparemment malade, ou de jeter des sorts dautres, via lInternet. Mais
comment cela est-il possible ?
Cela peut paratre paradoxal, mais comme nous lavons dj fait remarquer plus haut, le vrai matre
est en fait celui qui ne croit aucune magie, aucun don, aucune intervention surnaturelle. Seule la
froide, limplacable, linconfortable logique est de mise.
Le mode de pense dun programmeur combine des constructions intellectuelles complexes, similaires
celles quaccomplissent les mathmaticiens, les ingnieurs et les scientifiques. Comme le
mathmaticien, il utilise des langages formels pour dcrire des raisonnements (ou algorithmes).
Comme lingnieur, il conoit des dispositifs, il assemble des composants pour raliser des mcanismes
et il value leurs performances. Comme le scientifique, il observe le comportement de systmes
complexes, il cre des modles, il teste des prdictions.
Lactivit essentielle dun programmeur consiste rsoudre des problmes.
Il sagit l dune comptence de haut niveau, qui implique des capacits et des connaissances diverses :
tre capable de (re)formuler un problme de plusieurs manires diffrentes, tre capable dimaginer
des solutions innovantes et efficaces, tre capable dexprimer ces solutions de manire claire et
complte. Comme nous lavons dj voqu plus haut, il sagira souvent de mettre en lumire les
implications concrtes dune reprsentation mentale magique , simpliste ou trop abstraite.
La programmation dun ordinateur consiste en effet expliquer en dtail une machine ce quelle
doit faire, en sachant demble quelle ne peut pas vritablement comprendre un langage humain,
mais seulement effectuer un traitement automatique sur des squences de caractres. Il sagit la
plupart du temps de convertir un souhait exprim lorigine en termes magiques , en un vrai
raisonnement parfaitement structur et lucid dans ses moindres dtails, que lon appelle un algorithme.
Considrons par exemple une suite de nombres fournis dans le dsordre : 47, 19, 23, 15, 21, 36, 5, 12
Comment devons-nous nous y prendre pour obtenir dun ordinateur quil les remette dans lordre ?
Le souhait magique est de navoir qu cliquer sur un bouton, ou entrer une seule instruction au
clavier, pour quautomatiquement les nombres se mettent en place. Mais le travail du
sorcier-programmeur est justement de crer cette magie . Pour y arriver, il devra dcortiquer tout
ce quimplique pour nous une telle opration de tri (au fait, existe-t-il une mthode unique pour cela,
ou bien y en a-t-il plusieurs ?), et en traduire toutes les tapes en une suite dinstructions simples,
telles que par exemple comparer les deux premiers nombres, les changer sils ne sont pas dans
lordre souhait, recommencer avec le deuxime et le troisime, etc. .
Si les instructions ainsi mises en lumire sont suffisamment simples, il pourra alors les encoder dans la
machine en respectant de manire trs stricte un ensemble de conventions fixes lavance, que lon
appelle un langage informatique. Pour comprendre celui-ci, la machine sera pourvue dun
mcanisme qui dcode ces instructions en associant chaque mot du langage une action prcise.
Ainsi seulement, la magie pourra saccomplir.

Langage machine, langage de programmation

Langage machine, langage de programmation


strictement parler, un ordinateur nest rien dautre quune machine effectuant des oprations
simples sur des squences de signaux lectriques, lesquels sont conditionns de manire ne pouvoir
prendre que deux tats seulement (par exemple un potentiel lectrique maximum ou minimum). Ces
squences de signaux obissent une logique du type tout ou rien et peuvent donc tre considrs
conventionnellement comme des suites de nombres ne prenant jamais que les deux valeurs 0 et 1. Un
systme numrique ainsi limit deux chiffres est appel systme binaire.
Sachez ds prsent que dans son fonctionnement interne, un ordinateur est totalement incapable de
traiter autre chose que des nombres binaires. Toute information dun autre type doit tre convertie,
ou code, en format binaire. Cela est vrai non seulement pour les donnes que lon souhaite traiter (les
textes, les images, les sons, les nombres, etc.), mais aussi pour les programmes, cest--dire les
squences dinstructions que lon va fournir la machine pour lui dire ce quelle doit faire avec ces
donnes.
Le seul langage que lordinateur puisse vritablement comprendre est donc trs loign de ce
que nous utilisons nous-mmes. Cest une longue suite de 1 et de 0 (les bits ) souvent traits par
groupes de 8 (les octets ), 16, 32, ou mme 64. Ce langage machine est videmment presque
incomprhensible pour nous. Pour parler un ordinateur, il nous faudra utiliser des systmes de
traduction automatiques, capables de convertir en nombres binaires des suites de caractres formant
des mots-cls (anglais en gnral) qui seront plus significatifs pour nous.
Ces systmes de traduction automatique seront tablis sur la base de toute une srie de conventions,
dont il existera videmment de nombreuses variantes.
Le systme de traduction proprement dit sappellera interprteur ou bien compilateur, suivant la
mthode utilise pour effectuer la traduction. On appellera langage de programmation un ensemble de
mots-cls (choisis arbitrairement) associ un ensemble de rgles trs prcises indiquant comment
assembler ces mots pour former des phrases que linterprteur ou le compilateur puisse traduire
en langage machine (binaire).
Suivant son niveau dabstraction, on pourra dire dun langage quil est de bas niveau (ex : assembleur) ou de haut niveau (ex : Pascal, Perl, Smalltalk, Scheme, Lisp...). Un langage de bas niveau est
constitu dinstructions trs lmentaires, trs proches de la machine . Un langage de haut niveau
comporte des instructions plus abstraites, plus puissantes (et donc plus magiques ). Cela signifie
que chacune de ces instructions pourra tre traduite par linterprteur ou le compilateur en un grand
nombre dinstructions machine lmentaires.
Le langage que vous avez allez apprendre en premier est Python. Il sagit dun langage de haut niveau,
dont la traduction en code binaire est complexe et prend donc toujours un certain temps. Cela
pourrait paratre un inconvnient. En fait, les avantages que prsentent les langages de haut niveau
sont normes : il est beaucoup plus facile dcrire un programme dans un langage de haut niveau ;
lcriture du programme prend donc beaucoup moins de temps ; la probabilit dy faire des fautes est
nettement plus faible ; la maintenance (cest--dire lapport de modifications ultrieures) et la
recherche des erreurs (les bogues ) sont grandement facilites. De plus, un programme crit dans
un langage de haut niveau sera souvent portable, cest--dire que lon pourra le faire fonctionner sans

lcole des sorciers

gure de modifications sur des machines ou des systmes dexploitation diffrents. Un programme
crit dans un langage de bas niveau ne peut jamais fonctionner que sur un seul type de machine : pour
quune autre laccepte, il faut le rcrire entirement.
Dans ce que nous venons dexpliquer sommairement, vous aurez sans doute repr au passage de
nombreuses botes noires : interprteur, systme dexploitation, langage, instructions machine,
code binaire, etc. Lapprentissage de la programmation va vous permettre den entrouvrir
quelques-unes. Restez cependant conscient que vous narriverez pas les dcortiquer toutes. De
nombreux objets informatiques crs par dautres resteront probablement magiques pour vous
pendant longtemps ( commencer par le langage de programmation lui-mme, par exemple). Vous
devrez donc faire confiance leurs auteurs, quitte tre du parfois en constatant que cette
confiance nest pas toujours mrite. Restez donc vigilant, apprenez vrifier, vous documenter sans
cesse. Dans vos propres productions, soyez rigoureux et vitez tout prix la magie noire (les
programmes pleins dastuces tarabiscotes que vous tes seul comprendre) : un hacker digne de
confiance na rien cacher.

dition du code source Interprtation


Le programme tel que nous lcrivons dans un langage de programmation quelconque est
strictement parler un simple texte. Pour rdiger ce texte, on peut faire appel toutes sortes de
logiciels plus ou moins perfectionns, la condition quils ne produisent que du texte brut, cest--dire
sans mise en page particulire ni aucun attribut de style (pas de spcification de police, donc, pas de
gros titres, pas de gras, ni de soulign, ni ditalique, etc.)3.
Le texte ainsi produit est ce que nous appellerons dsormais un code source.
Comme nous lavons dj voqu plus haut, le code source doit tre traduit en une suite dinstructions
binaires directement comprhensibles par la machine : le code objet . Dans le cas de Python, cette
traduction est prise en charge par un interprteur assist dun pr-compilateur. Cette technique hybride
(galement utilise par le langage Java) vise exploiter au maximum les avantages de linterprtation
et de la compilation, tout en minimisant leurs inconvnients respectifs. Veuillez consulter un ouvrage
dinformatique gnrale si vous voulez en savoir davantage sur ces deux techniques.
Sachez simplement ce sujet que vous pourrez raliser des programmes extrmement performants
avec Python, mme sil est indiscutable quun langage strictement compil tel que le C peut toujours
faire mieux en termes de rapidit dexcution.

Mise au point dun programme Recherche des erreurs (debug)


La programmation est une dmarche trs complexe, et comme cest le cas dans toute activit humaine,
on y commet de nombreuses erreurs. Pour des raisons anecdotiques, les erreurs de programmation
sappellent des bugs (ou bogues , en Franais)4, et lensemble des techniques que lon met en
uvre pour les dtecter et les corriger sappelle debug (ou dbogage ).
3 Ces

logiciels sont appels des diteurs de texte. Mme sils proposent divers automatismes, et sont souvent
capables de mettre en vidence certains lments du texte trait (coloration syntaxique, par exemple), ils ne
produisent strictement que du texte non format. Ils sont donc assez diffrents des logiciels de traitement de
texte, dont la fonction consiste justement mettre en page et ornementer un texte avec des attributs de
toute sorte, de manire le rendre aussi agrable lire que possible.

Mise au point dun programme Recherche des erreurs (debug)

En fait, il peut exister dans un programme trois types derreurs assez diffrentes, et il convient que
vous appreniez bien les distinguer.

Erreurs de syntaxe
Python ne peut excuter un programme que si sa syntaxe est parfaitement correcte. Dans le cas
contraire, le processus sarrte et vous obtenez un message derreur. Le terme syntaxe se rfre aux
rgles que les auteurs du langage ont tablies pour la structure du programme.
Tout langage comporte sa syntaxe. Dans la langue franaise, par exemple, une phrase doit toujours
commencer par une majuscule et se terminer par un point. ainsi cette phrase comporte deux erreurs
de syntaxe.
Dans les textes ordinaires, la prsence de quelques petites fautes de syntaxe par-ci par-l na
gnralement pas dimportance. Il peut mme arriver (en posie, par exemple), que des fautes de
syntaxe soient commises volontairement. Cela nempche pas que lon puisse comprendre le texte.
Dans un programme dordinateur, par contre, la moindre erreur de syntaxe produit invariablement un
arrt de fonctionnement (un plantage ) ainsi que laffichage dun message derreur. Au cours des
premires semaines de votre carrire de programmeur, vous passerez certainement pas mal de temps
rechercher vos erreurs de syntaxe. Avec de lexprience, vous en commettrez beaucoup moins.
Gardez lesprit que les mots et les symboles utiliss nont aucune signification en eux-mmes : ce ne
sont que des suites de codes destins tre convertis automatiquement en nombres binaires. Par
consquent, il vous faudra tre trs attentifs respecter scrupuleusement la syntaxe du langage.
Finalement, souvenez-vous que tous les dtails ont de limportance. Il faudra en particulier faire trs
attention la casse (cest--dire lemploi des majuscules et des minuscules) et la ponctuation. Toute
erreur ce niveau (mme minime en apparence, tel loubli dune virgule, par exemple) peut modifier
considrablement la signification du code, et donc le droulement du programme.
Il est heureux que vous fassiez vos dbuts en programmation avec un langage interprt tel que
Python. La recherche des erreurs y est facile et rapide. Avec les langages compils (tel C++), il vous
faudrait recompiler lintgralit du programme aprs chaque modification, aussi minime soit-elle.

Erreurs smantiques
Le second type derreur est lerreur smantique ou erreur de logique. Sil existe une erreur de ce type
dans un de vos programmes, celui-ci sexcute parfaitement, en ce sens que vous nobtenez aucun
message derreur, mais le rsultat nest pas celui que vous attendiez : vous obtenez autre chose.
En ralit, le programme fait exactement ce que vous lui avez dit de faire. Le problme est que ce que
vous lui avez dit de faire ne correspond pas ce que vous vouliez quil fasse. La squence
4 bug

est lorigine un terme anglais servant dsigner de petits insectes gnants, tels les punaises. Les
premiers ordinateurs fonctionnaient laide de lampes radios qui ncessitaient des tensions lectriques
assez leves. Il est arriv plusieurs reprises que des petits insectes sintroduisent dans cette circuiterie
complexe et se fassent lectrocuter, leurs cadavres calcins provoquant alors des court-circuits et donc des
pannes incomprhensibles.
Le mot franais bogue a t choisi par homonymie approximative. Il dsigne la coque pineuse de la
chtaigne.

lcole des sorciers

dinstructions de votre programme ne correspond pas lobjectif poursuivi. La smantique (la logique)
est incorrecte.
Rechercher des fautes de logique peut tre une tche ardue. Cest l que se rvlera votre aptitude
dmonter toute forme rsiduelle de pense magique dans vos raisonnements. Il vous faudra
analyser patiemment ce qui sort de la machine et tcher de vous reprsenter une par une les
oprations quelle a effectues, la suite de chaque instruction.

Erreurs lexcution
Le troisime type derreur est lerreur en cours dexcution (Run-time error), qui apparat seulement
lorsque votre programme fonctionne dj, mais que des circonstances particulires se prsentent (par
exemple, votre programme essaie de lire un fichier qui nexiste plus). Ces erreurs sont galement
appeles des exceptions, parce quelles indiquent en gnral que quelque chose dexceptionnel (et de
malencontreux) sest produit. Vous rencontrerez ce type derreurs lorsque vous programmerez des
projets de plus en plus volumineux, et vous apprendrez plus loin dans ce cours quil existe des
techniques particulires pour les grer.

Recherche des erreurs et exprimentation


Lune des comptences les plus importantes acqurir au cours de votre apprentissage est celle qui
consiste dboguer efficacement un programme. Il sagit dune activit intellectuelle parfois nervante
mais toujours trs riche, dans laquelle il faut faire montre de beaucoup de perspicacit.
Ce travail ressemble par bien des aspects une enqute policire. Vous examinez un ensemble de faits,
et vous devez mettre des hypothses explicatives pour reconstituer les processus et les vnements
qui ont logiquement entran les rsultats que vous constatez.
Cette activit sapparente aussi au travail exprimental en sciences. Vous vous faites une premire
ide de ce qui ne va pas, vous modifiez votre programme et vous essayez nouveau. Vous avez mis
une hypothse, qui vous permet de prdire ce que devra donner la modification. Si la prdiction se
vrifie, alors vous avez progress dun pas sur la voie dun programme qui fonctionne. Si la prdiction
se rvle fausse, alors il vous faut mettre une nouvelle hypothse. Comme la bien dit Sherlock
Holmes : Lorsque vous avez limin limpossible, ce qui reste, mme si cest improbable, doit tre la
vrit (A. Conan Doyle, Le signe des quatre).
Pour certaines personnes, programmer et dboguer signifient exactement la mme chose. Ce
quelles veulent dire par l est que lactivit de programmation consiste en fait modifier, corriger
sans cesse un mme programme, jusqu ce quil se comporte finalement comme vous le vouliez. Lide
est que la construction dun programme commence toujours par une bauche qui fait dj quelque
chose (et qui est donc dj dbogue), laquelle on ajoute couche par couche de petites modifications,
en corrigeant au fur et mesure les erreurs, afin davoir de toute faon chaque tape du processus
un programme qui fonctionne.
Par exemple, vous savez que Linux est un systme dexploitation (et donc un gros logiciel) qui
comporte des milliers de lignes de code. Au dpart, cependant, cela a commenc par un petit
programme simple que Linus Torvalds avait dvelopp pour tester les particularits du processeur In-

Recherche des erreurs et exprimentation

tel 80386. Daprs Larry Greenfield ( The Linux users guide , beta version 1) : Lun des premiers
projets de Linus tait un programme destin convertir une chane de caractres AAAA en BBBB. Cest
cela qui plus tard finit par devenir Linux ! .
Ce qui prcde ne signifie pas que nous voulions vous pousser programmer par approximations
successives, partir dune vague ide. Lorsque vous dmarrerez un projet de programmation dune
certaine importance, il faudra au contraire vous efforcer dtablir le mieux possible un cahier des
charges dtaill, lequel sappuiera sur un plan solidement construit pour lapplication envisage.
Diverses mthodes existent pour effectuer cette tche danalyse, mais leur tude sort du cadre de ces
notes. Nous vous prsenterons cependant plus loin (voir chapitre 15) quelques ides de base.

2
Premiers pas

La programmation est donc lart de commander un ordinateur de faire exactement ce que vous
voulez, et Python compte parmi les langages quil est capable de comprendre pour recevoir vos
ordres. Nous allons essayer cela tout de suite avec des ordres trs simples concernant des nombres,
puisque les nombres constituent son matriau de prdilection. Nous allons lui fournir nos
premires instructions , et prciser au passage la dfinition de quelques termes essentiels du
vocabulaire informatique, que vous rencontrerez constamment dans la suite de cet ouvrage.
Comme nous lavons expliqu dans la prface (voir : Versions du langage, page XII), nous avons pris
le parti dutiliser dans ce cours la nouvelle version 3 de Python, laquelle a introduit quelques
changements syntaxiques par rapport aux versions prcdentes. Dans la mesure du possible, nous
vous indiquerons ces diffrences dans le texte, afin que vous puissiez sans problme analyser ou
utiliser danciens programmes crits pour Python 1 ou 2.

Calculer avec Python


Python prsente la particularit de pouvoir tre utilis de plusieurs manires diffrentes. Vous allez
dabord lutiliser en mode interactif, cest--dire de manire dialoguer avec lui directement depuis le
clavier. Cela vous permettra de dcouvrir trs vite un grand nombre de fonctionnalits du langage.
Dans un second temps, vous apprendrez comment crer vos premiers programmes (scripts) et les
sauvegarder sur disque.
Linterprteur peut tre lanc directement depuis la ligne de commande (dans un shell Linux, ou
bien dans une fentre DOS sous Windows) : il suffit dy taper la commande python3 (en supposant que le
logiciel lui-mme ait t correctement install, et quil sagisse dune des dernires versions de
Python), ou python (si la version de Python installe sur votre ordinateur est antrieure la version
3.0).
Si vous utilisez une interface graphique telle que Windows, Gnome, WindowMaker ou KDE, vous
prfrerez vraisemblablement travailler dans une fentre de terminal , ou encore dans un environ-

12

Premiers pas

nement de travail spcialis tel que IDLE. Voici par exemple ce qui apparat dans une fentre de
terminal Gnome (sous Ubuntu Linux)5 :

Avec IDLE sous Windows, votre environnement de travail ressemblera celui-ci :

Les trois caractres suprieur constituent le signal dinvite, ou prompt principal, lequel vous
indique que Python est prt excuter une commande.
Par exemple, vous pouvez tout de suite utiliser linterprteur comme une simple calculatrice de
bureau. Veuillez donc vous-mme tester les commandes ci-dessous (Prenez lhabitude dutiliser votre
cahier dexercices pour noter les rsultats qui apparaissent lcran) :
>>> 5+3
>>> 2 9

# les espaces sont optionnels

>>> 7 + 3 * 4

# la hirarchie des oprations mathmatiques


# est-elle respecte ?

>>> (7+3)*4
>>> 20 / 3

# attention : ceci fonctionnerait diffremment sous Python 2

>>> 20 // 3
5 Sous

Windows, vous aurez surtout le choix entre l environnement IDLE dvelopp par Guido Van Rossum,
auquel nous donnons nous-mme la prfrence, et PythonWin, une interface de dveloppement dveloppe
par Mark Hammond. Dautres environnements de travail plus sophistiqus existent aussi, tels l excellent Boa
Constructor par exemple (qui fonctionne de faon trs similaire Delphi), mais nous estimons qu ils ne
conviennent gure aux dbutants. Pour tout renseignement complmentaire, veuillez consulter le site Web
de Python.
Sous Linux, nous prfrons personnellement travailler dans une simple fentre de terminal pour lancer
linterprteur Python ou lexcution des scripts, et faire appel un diteur de texte ordinaire tel que Gedit,
Kate, ou un peu plus spcialis comme Geany pour ldition de ces derniers.

Calculer avec Python

13

Comme vous pouvez le constater, les oprateurs arithmtiques pour laddition, la soustraction, la
multiplication et la division sont respectivement +, -, * et /. Les parenthses ont la fonction attendue.
Sous Python 3, loprateur de division / effectue une division relle. Si vous souhaitez
obtenir une division entire (cest--dire dont le rsultat tronqu ne peut tre quun
entier), vous devez utiliser loprateur //. Veuillez noter que ceci est lun des
changements de syntaxe apports la version 3 de Python, par rapport aux versions
prcdentes. Si vous utilisez lune de ces versions, sachez que loprateur / y effectue
par dfaut une division entire, si on lui fournit des arguments qui sont eux-mmes des
entiers, et une division relle, si au moins lun des arguments est un rel. Cet ancien
comportement de Python a t heureusement abandonn, car il conduisait parfois des
bogues difficilement reprables.
>>> 20.5 / 3
>>> 8,7 / 5

# Erreur !

Veuillez remarquer au passage une rgle qui vaut dans tous les langages de programmation : les
conventions mathmatiques de base sont celles qui sont en vigueur dans les pays anglophones. Le
sparateur dcimal y est donc toujours un point, et non une virgule comme chez nous. Notez aussi que
dans le monde de linformatique, les nombres rels sont souvent dsigns comme des nombres
virgule flottante (floating point numbers).

Donnes et variables
Nous aurons loccasion de dtailler plus loin les diffrents types de donnes numriques. Mais avant
cela, nous pouvons ds prsent aborder un concept de grande importance.
Lessentiel du travail effectu par un programme dordinateur consiste manipuler des donnes. Ces
donnes peuvent tre trs diverses (tout ce qui est numrisable, en fait6), mais dans la mmoire de lordinateur elles se ramnent toujours en dfinitive une suite finie de nombres binaires.
Pour pouvoir accder aux donnes, le programme dordinateur (quel que soit le langage dans lequel il
est crit) fait abondamment usage dun grand nombre de variables de diffrents types.
Une variable apparat dans un langage de programmation sous un nom de variable peu prs
quelconque (voir ci-aprs), mais pour lordinateur il sagit dune rfrence dsignant une adresse
mmoire, cest--dire un emplacement prcis dans la mmoire vive.
cet emplacement est stocke une valeur bien dtermine. Cest la donne proprement dite, qui est
donc stocke sous la forme dune suite de nombres binaires, mais qui nest pas ncessairement un
nombre aux yeux du langage de programmation utilis. Cela peut tre en fait peu prs nimporte
quel objet susceptible dtre plac dans la mmoire dun ordinateur, par exemple : un nombre
entier, un nombre rel, un nombre complexe, un vecteur, une chane de caractres typographiques,
un tableau, une fonction, etc.
Pour distinguer les uns des autres ces divers contenus possibles, le langage de programmation fait
usage de diffrents types de variables (le type entier, le type rel, le type chane de caractres, le type
liste, etc.). Nous allons expliquer tout cela dans les pages suivantes.
6 Que

peut-on numriser au juste ? Voil une question trs importante, qu il vous faudra dbattre dans votre
cours dinformatique gnrale.

14

Premiers pas

Noms de variables et mots rservs


Les noms de variables sont des noms que vous choisissez vous-mme assez librement. Efforcez-vous
cependant de bien les choisir : de prfrence assez courts, mais aussi explicites que possible, de
manire exprimer clairement ce que la variable est cense contenir. Par exemple, des noms de
variables tels que altitude, altit ou alt conviennent mieux que x pour exprimer une altitude.
Un bon programmeur doit veiller ce que ses lignes dinstructions soient faciles lire.
Sous Python, les noms de variables doivent en outre obir quelques rgles simples :

Un nom de variable est une squence de lettres (a z , A Z) et de chiffres (0 9), qui doit
toujours commencer par une lettre.
Seules les lettres ordinaires sont autorises. Les lettres accentues, les cdilles, les espaces, les
caractres spciaux tels que $, #, @, etc. sont interdits, lexception du caractre _ (soulign).
La casse est significative (les caractres majuscules et minuscules sont distingus).
Attention : Joseph, joseph, JOSEPH sont donc des variables diffrentes. Soyez attentifs !
Prenez lhabitude dcrire lessentiel des noms de variables en caractres minuscules (y compris la
premire lettre7). Il sagit dune simple convention, mais elle est largement respecte. Nutilisez les
majuscules qu lintrieur mme du nom, pour en augmenter ventuellement la lisibilit, comme dans
tableDesMatieres.
En plus de ces rgles, il faut encore ajouter que vous ne pouvez pas utiliser comme nom de variables
les 33 mots rservs ci-dessous (ils sont utiliss par le langage lui-mme) :
and

as

assert

break

class

continue

def

del

elif

else

except

False

finally

for

from

global

if

import

in

is

lambda

None

nonlocal

not

or

pass

raise

return

True

try

while

with

yield

Affectation (ou assignation)


Nous savons dsormais comment choisir judicieusement un nom de variable. Voyons prsent
comment nous pouvons dfinir une variable et lui affecter une valeur. Les termes affecter une valeur
ou assigner une valeur une variable sont quivalents. Ils dsignent lopration par laquelle on
tablit un lien entre le nom de la variable et sa valeur (son contenu).
En Python comme dans de nombreux autres langages, lopration daffectation est reprsente par le
signe gale8 :
7 Les

noms commenant par une majuscule ne sont pas interdits, mais l usage veut quon le rserve plutt
aux variables qui dsignent des classes (le concept de classe sera abord plus loin dans cet ouvrage). Il arrive
aussi que lon crive entirement en majuscules certaines variables que lon souhaite traiter comme des
pseudo-constantes (cest--dire des variables que lon vite de modifier au cours du programme).
8 Il faut bien comprendre qu il ne sagit en aucune faon d une galit, et que l on aurait trs bien pu choisir
un autre symbolisme, tel que n 7 par exemple, comme on le fait souvent dans certains pseudo-langages
servant dcrire des algorithmes, pour bien montrer qu il sagit de relier un contenu (la valeur 7) un
contenant (la variable n).

Affectation (ou assignation)


>>> n = 7
>>> msg = "Quoi de neuf ?"
>>> pi = 3.14159

15
# dfinir la variable n et lui donner la valeur 7
# affecter la valeur "Quoi de neuf ?" msg
# assigner sa valeur la variable pi

Les exemples ci-dessus illustrent des instructions daffectation Python tout fait classiques. Aprs
quon les ait excutes, il existe dans la mmoire de lordinateur, des endroits diffrents :

trois noms de variables, savoir n, msg et pi ;


trois squences doctets, o sont encodes le nombre entier 7, la chane de caractres Quoi de
neuf ? et le nombre rel 3,14159.
Les trois instructions daffectation ci-dessus ont eu pour effet chacune de raliser plusieurs oprations
dans la mmoire de lordinateur :
crer et mmoriser un nom de variable ;
lui attribuer un type bien dtermin (ce point sera explicit la page suivante) ;
crer et mmoriser une valeur particulire ;
tablir un lien (par un systme interne de pointeurs) entre le nom de la variable et lemplacement
mmoire de la valeur correspondante.
On peut mieux se reprsenter tout cela par un diagramme dtat tel que celui-ci :

Les trois noms de variables sont des rfrences, mmorises dans une zone particulire de la mmoire
que lon appelle espace de noms, alors que les valeurs correspondantes sont situes ailleurs, dans des
emplacements parfois fort loigns les uns des autres. Nous aurons loccasion de prciser ce concept
plus loin dans ces pages.

Afficher la valeur dune variable


la suite de lexercice ci-dessus, nous disposons donc des trois variables n, msg et pi.
Pour afficher leur valeur lcran, il existe deux possibilits. La premire consiste entrer au clavier
le nom de la variable, puis <Enter>. Python rpond en affichant la valeur correspondante :
>>> n
7
>>> msg
'Quoi de neuf ?'
>>> pi
3.14159

Il sagit cependant l dune fonctionnalit secondaire de linterprteur, qui est destine vous faciliter
la vie lorsque vous faites de simples exercices la ligne de commande. lintrieur dun programme,
vous utiliserez toujours la fonction print()9 :
9 Les fonctions seront dfinies en dtail dans les

chapitres 6 et 7 (voir page 49).

16

Premiers pas

>>> print(msg)
Quoi de neuf ?
>>> print(n)
7

Remarquez la subtile diffrence dans les affichages obtenus avec chacune des deux mthodes. La
fonction print() naffiche strictement que la valeur de la variable, telle quelle a t encode, alors
que lautre mthode (celle qui consiste entrer seulement le nom de la variable) affiche aussi des
apostrophes afin de vous rappeler que la variable traite est du type chane de caractres (nous y
reviendrons).
Dans les versions de Python antrieures la version 3.0, le rle de la fonction print()
tait assur par une instruction print particulire, faisant dailleurs lobjet dun mot
rserv (voir page 14). Cette instruction sutilisait sans parenthses. Dans les exercices
prcdents, il fallait donc entrer print n ou print msg. Si vous essayez plus tard de
faire fonctionner sous Python 3 des programmes crits dans lune ou lautre version
ancienne, sachez donc que vous devrez ajouter des parenthses aprs chaque
instruction print afin de convertir celle-ci en fonction (des utilitaires permettent de
raliser cela automatiquement).
Dans ces mmes versions anciennes, les chanes de caractres taient traites
diffremment (nous en reparlerons en dtail plus loin). Suivant la configuration de votre
ordinateur, vous pouviez alors rencontrer quelques effets bizarres avec les chanes
contenant des caractres accentus, tels que :
>>> msg = "Mon prnom est Chimne"
>>> msg
'Mon pr\xe9nom est Chim\xe8ne'
Ces bizarreries appartiennent dsormais au pass, mais nous verrons plus loin quun
programmeur digne de ce nom doit savoir de quelle manire sont encods les caractres
typographiques rencontrs dans diffrentes sources de donnes, car les normes
dfinissant ces encodages ont chang au cours des annes, et il faut connatre les
techniques qui permettent de les convertir.

Typage des variables


Sous Python, il nest pas ncessaire dcrire des lignes de programme spcifiques pour dfinir le type
des variables avant de pouvoir les utiliser. Il vous suffit en effet dassigner une valeur un nom de
variable pour que celle-ci soit automatiquement cre avec le type qui correspond au mieux la valeur
fournie. Dans lexercice prcdent, par exemple, les variables n, msg et pi ont t cres
automatiquement chacune avec un type diffrent ( nombre entier pour n, chane de caractres
pour msg, nombre virgule flottante (ou float , en anglais) pour pi).
Ceci constitue une particularit intressante de Python, qui le rattache une famille particulire de
langages o lon trouve aussi par exemple Lisp, Scheme, et quelques autres. On dira ce sujet que le
typage des variables sous Python est un typage dynamique, par opposition au typage statique qui est de rgle
par exemple en C++ ou en Java. Dans ces langages, il faut toujours par des instructions distinctes
dabord dclarer (dfinir) le nom et le type des variables, et ensuite seulement leur assigner un
contenu, lequel doit bien entendu tre compatible avec le type dclar.
Le typage statique est prfrable dans le cas des langages compils, parce quil permet doptimiser
lopration de compilation (dont le rsultat est un code binaire fig ).

Typage des variables

17

Le typage dynamique quant lui permet dcrire plus aisment des constructions logiques de niveau
lev (mtaprogrammation, rflexivit), en particulier dans le contexte de la programmation oriente
objet (polymorphisme). Il facilite galement lutilisation de structures de donnes trs riches telles que
les listes et les dictionnaires.

Affectations multiples
Sous Python, on peut assigner une valeur plusieurs variables simultanment. Exemple :
>>> x = y = 7
>>> x
7
>>> y
7

On peut aussi effectuer des affectations parallles laide dun seul oprateur :
>>> a, b = 4, 8.33
>>> a
4
>>> b
8.33

Dans cet exemple, les variables a et b prennent simultanment les nouvelles valeurs 4 et 8,33.
Les francophones que nous sommes avons pour habitude dutiliser la virgule comme
sparateur dcimal, alors que les langages de programmation utilisent toujours la
convention en vigueur dans les pays de langue anglaise, cest--dire le point dcimal. La
virgule, quant elle, est trs gnralement utilise pour sparer diffrents lments
(arguments, etc.) comme on le voit dans notre exemple, pour les variables elles-mmes
ainsi que pour les valeurs quon leur attribue.

Exercices
2.1 Dcrivez le plus clairement et le plus compltement possible ce qui se passe chacune des
trois lignes de lexemple ci-dessous :
>>> largeur = 20
>>> hauteur = 5 * 9.3
>>> largeur * hauteur
930

2.2

Assignez les valeurs respectives 3, 5, 7 trois variables a, b, c.


Effectuez lopration a-b//c. Interprtez le rsultat obtenu.

Oprateurs et expressions
On manipule les valeurs et les variables qui les rfrencent en les combinant avec des oprateurs pour
former des expressions. Exemple :
a, b = 7.3, 12
y = 3*a + b/5

Dans cet exemple, nous commenons par affecter aux variables a et b les valeurs 7,3 et 12.
Comme dj expliqu prcdemment, Python assigne automatiquement le type rel la variable a,
et le type entier la variable b.

18

Premiers pas

La seconde ligne de lexemple consiste affecter une nouvelle variable y le rsultat dune expression
qui combine les oprateurs * , + et / avec les oprandes a, b, 3 et 5. Les oprateurs sont les symboles spciaux utiliss pour reprsenter des oprations mathmatiques simples, telles laddition ou la
multiplication. Les oprandes sont les valeurs combines laide des oprateurs.
Python value chaque expression quon lui soumet, aussi complique soit-elle, et le rsultat de cette
valuation est toujours lui-mme une valeur. cette valeur, il attribue automatiquement un type,
lequel dpend de ce quil y a dans lexpression. Dans lexemple ci-dessus, y sera du type rel, parce que
lexpression value pour dterminer sa valeur contient elle-mme au moins un rel.
Les oprateurs Python ne sont pas seulement les quatre oprateurs mathmatiques de base. Nous
avons dj signal lexistence de loprateur de division entire //. Il faut encore ajouter loprateur
** pour lexponentiation, ainsi quun certain nombre doprateurs logiques, des oprateurs agissant
sur les chanes de caractres, des oprateurs effectuant des tests didentit ou dappartenance, etc.
Nous reparlerons de tout cela au fil des pages suivantes.
Signalons au passage la disponibilit de loprateur modulo, reprsent par le caractre typographique
%. Cet oprateur fournit le reste de la division entire dun nombre par un autre. Essayez par exemple :
>>> 10 % 3
>>> 10 % 5

# (et prenez note de ce qui se passe !)

Cet oprateur vous sera trs utile plus loin, notamment pour tester si un nombre a est divisible par un
nombre b. Il suffira en effet de vrifier que a % b donne un rsultat gal zro.
Exercice
2.3 Testez les lignes dinstructions suivantes. Dcrivez ce qui se passe :
>>>
>>>
>>>
>>>

r , pi = 12, 3.14159
s = pi * r**2
print(s)
print(type(r), type(pi), type(s))

Quelle est, votre avis, lutilit de la fonction type() ?


(Note : les fonctions seront dcrites en dtail aux chapitres 6 et 7.)

Priorit des oprations


Lorsquil y a plus dun oprateur dans une expression, lordre dans lequel les oprations doivent tre
effectues dpend de rgles de priorit. Sous Python, les rgles de priorit sont les mmes que celles qui
vous ont t enseignes au cours de mathmatique. Vous pouvez les mmoriser aisment laide dun
truc mnmotechnique, lacronyme PEMDAS :

P pour parenthses. Ce sont elles qui ont la plus haute priorit. Elles vous permettent donc de forcer lvaluation dune expression dans lordre que vous voulez.
Ainsi 2*(3-1) = 4 , et (1+1)**(5-2) = 8.
E pour exposants. Les exposants sont valus ensuite, avant les autres oprations.
Ainsi 2**1+1 = 3 (et non 4), et 3*1**10 = 3 (et non 59049 !).

Priorit des oprations

19

M et D pour multiplication et division, qui ont la mme priorit. Elles sont values avant laddition A
et la soustraction S, lesquelles sont donc effectues en dernier lieu.
Ainsi 2*3-1 = 5 (plutt que 4), et 2/3-1 = -0.3333... (plutt que 1.0).
Si deux oprateurs ont la mme priorit, lvaluation est effectue de gauche droite.
Ainsi dans lexpression 59*100//60, la multiplication est effectue en premier, et la machine doit
donc ensuite effectuer 5900//60, ce qui donne 98. Si la division tait effectue en premier, le
rsultat serait 59 (rappelez-vous ici que loprateur // effectue une division entire, et vrifiez en
effectuant 59*(100//60)).

Composition
Jusquici nous avons examin les diffrents lments dun langage de programmation, savoir : les variables, les expressions et les instructions, mais sans traiter de la manire dont nous pouvons les
combiner les unes avec les autres.
Or lune des grandes forces dun langage de programmation de haut niveau est quil permet de
construire des instructions complexes par assemblage de fragments divers. Ainsi par exemple, si vous
savez comment additionner deux nombres et comment afficher une valeur, vous pouvez combiner ces
deux instructions en une seule :
>>> print(17 + 3)
>>> 20

Cela na lair de rien, mais cette fonctionnalit qui parat si vidente va vous permettre de programmer
des algorithmes complexes de faon claire et concise. Exemple :
>>> h, m, s = 15, 27, 34
>>> print("nombre de secondes coules depuis minuit = ", h*3600 + m*60 + s)

Attention, cependant : il y a une limite ce que vous pouvez combiner ainsi :


Dans une expression, ce que vous placez la gauche du signe gale doit toujours tre une variable, et
non une expression. Cela provient du fait que le signe gale na pas ici la mme signification quen
mathmatique : comme nous lavons dj signal, il sagit dun symbole daffectation (nous plaons un
certain contenu dans une variable) et non un symbole dgalit. Le symbole dgalit (dans un test
conditionnel, par exemple) sera voqu un peu plus loin.
Ainsi par exemple, linstruction

m + 1 = b

est tout fait illgale.

Par contre, crire a = a + 1 est inacceptable en mathmatique, alors que cette forme dcriture est
trs frquente en programmation. Linstruction a = a + 1 signifie en loccurrence augmenter la
valeur de la variable a dune unit (ou encore : incrmenter a ).
Nous aurons loccasion de revenir bientt sur ce sujet. Mais auparavant, il nous faut encore aborder un
autre concept de grande importance.

3
Contrle du flux dexcution

Dans notre premier chapitre, nous avons vu que lactivit essentielle dun programmeur est la
rsolution de problmes. Or, pour rsoudre un problme informatique, il faut toujours effectuer une
srie dactions dans un certain ordre. La description structure de ces actions et de lordre dans
lequel il convient de les effectuer sappelle un algorithme.
Le chemin suivi par Python travers un programme est appel un flux dexcution, et les
constructions qui le modifient sont appeles des instructions de contrle de flux.
Les structures de contrle sont les groupes dinstructions qui dterminent lordre dans lequel les
actions sont effectues. En programmation moderne, il en existe seulement trois : la squence10 et la
slection, que nous allons dcrire dans ce chapitre, et la rptition que nous aborderons au chapitre
suivant.

Squence dinstructions
Sauf mention explicite, les instructions dun programme sexcutent les unes aprs les autres, dans
lordre o elles ont t crites lintrieur du script.
Cette affirmation peut vous paratre banale et vidente premire vue. Lexprience montre
cependant quun grand nombre derreurs smantiques dans les programmes dordinateur sont la
consquence dune mauvaise disposition des instructions. Plus vous progresserez dans lart de la
programmation, plus vous vous rendrez compte quil faut tre extrmement attentif lordre dans
lequel vous placez vos instructions les unes derrire les autres. Par exemple, dans la squence dinstructions suivantes :
>>>
>>>
>>>
>>>

a, b = 3, 7
a = b
b = a
print(a, b)

Vous obtiendrez un rsultat contraire si vous intervertissez les 2e et 3e lignes.


10 Tel

quil est utilis ici, le terme de squence dsigne donc une srie dinstructions qui se suivent. Nous
prfrerons dans la suite de cet ouvrage rserver ce terme un concept Python prcis, lequel englobe les
chanes de caractres, les tuples et les listes (voir plus loin).

22

Contrle du flux dexcution

Python excute normalement les instructions de la premire la dernire, sauf lorsquil rencontre une
instruction conditionnelle comme linstruction if dcrite ci-aprs (nous en rencontrerons dautres plus
loin, notamment propos des boucles de rptition). Une telle instruction va permettre au
programme de suivre diffrents chemins suivant les circonstances.

Slection ou excution conditionnelle


Si nous voulons pouvoir crire des applications vritablement utiles, il nous faut des techniques
permettant daiguiller le droulement du programme dans diffrentes directions, en fonction des
circonstances rencontres. Pour ce faire, nous devons disposer dinstructions capables de tester une
certaine condition et de modifier le comportement du programme en consquence.
La plus simple de ces instructions conditionnelles est linstruction if. Pour exprimenter son
fonctionnement, veuillez entrer dans votre diteur Python les deux lignes suivantes :
>>> a = 150
>>> if (a > 100):
...

La premire commande affecte la valeur 150 la variable a. Jusquici rien de nouveau.


Lorsque vous finissez dentrer la seconde ligne, par contre, vous constatez que Python ragit dune
nouvelle manire. En effet, et moins que vous nayez oubli le caractre : la fin de la ligne, vous
constatez que le prompt principal (>>>) est maintenant remplac par un prompt secondaire constitu de
trois points11.
Si votre diteur ne le fait pas automatiquement, vous devez prsent effectuer une tabulation (ou
entrer 4 espaces) avant dentrer la ligne suivante, de manire ce que celle-ci soit indente (cest--dire
en retrait) par rapport la prcdente. Votre cran devrait se prsenter maintenant comme suit :
>>> a = 150
>>> if (a > 100):
...
print("a dpasse la centaine")
...

Frappez encore une fois <Enter>. Le programme sexcute, et vous obtenez :


a dpasse la centaine

Recommencez le mme exercice, mais avec a


fiche plus rien.

= 20

en guise de premire ligne : cette fois Python naf-

Lexpression que vous avez place entre parenthses aprs if est ce que nous appellerons dsormais
une condition. Linstruction if permet de tester la validit de cette condition. Si la condition est vraie,
alors linstruction que nous avons indente aprs le : est excute. Si la condition est fausse, rien ne se
passe. Notez que les parenthses utilises ici avec linstruction if sont optionnelles : nous les avons
utilises pour amliorer la lisibilit. Dans dautres langages, il se peut quelles soient obligatoires.
Recommencez encore, en ajoutant deux lignes comme indiqu ci-dessous. Veillez bien ce que la
quatrime ligne dbute tout fait gauche (pas dindentation), mais que la cinquime soit nouveau
indente (de prfrence avec un retrait identique celui de la troisime) :
11 Dans certaines

versions de lditeur Python pour Windows, le prompt secondaire napparat pas.

Slection ou excution conditionnelle

23

>>> a = 20
>>> if (a > 100):
...
print("a dpasse la centaine")
... else:
...
print("a ne dpasse pas cent")
...

Frappez <Enter> encore une fois. Le programme sexcute, et affiche cette fois :
a ne dpasse pas cent

Comme vous laurez certainement dj compris, linstruction else ( sinon , en anglais) permet de
programmer une excution alternative, dans laquelle le programme doit choisir entre deux
possibilits. On peut faire mieux encore en utilisant aussi linstruction elif (contraction de else if ) :
>>>
>>>
...
...
...
...
...
...

a = 0
if a > 0 :
print("a est positif")
elif a < 0 :
print("a est ngatif")
else:
print("a est nul")

Oprateurs de comparaison
La condition value aprs linstruction if peut contenir les oprateurs de comparaison suivants :
x
x
x
x
x
x

== y
!= y
> y
< y
>= y
<= y

#
#
#
#
#
#

x
x
x
x
x
x

est
est
est
est
est
est

gal y
diffrent de y
plus grand que y
plus petit que y
plus grand que, ou gal y
plus petit que, ou gal y

Exemple
>>> a = 7
>>> if (a % 2 == 0):
...
print("a est pair")
...
print("parce que le reste de sa division par 2 est nul")
... else:
...
print("a est impair")
...

Notez bien que loprateur de comparaison pour lgalit de deux valeurs est constitu de deux signes
gale et non dun seul12. Le signe gale utilis seul est un oprateur daffectation, et non un
oprateur de comparaison. Vous retrouverez le mme symbolisme en C++ et en Java.

Instructions composes blocs dinstructions


La construction que vous avez utilise avec linstruction if est votre premier exemple dinstruction
compose. Vous en rencontrerez bientt dautres. Sous Python, les instructions composes ont tou12 Rappel
a % 2

: loprateur % est loprateur modulo : il calcule le reste dune division entire. Ainsi par exemple,
fournit le reste de la division de a par 2.

24

Contrle du flux dexcution

jours la mme structure : une ligne den-tte termine par un double point, suivie dune ou de
plusieurs instructions indentes sous cette ligne den-tte. Exemple :
Ligne den-tte:
premire instruction du bloc
... ...
... ...
dernire instruction du bloc

Sil y a plusieurs instructions indentes sous la ligne den-tte, elles doivent ltre exactement au mme
niveau (comptez un dcalage de 4 caractres, par exemple). Ces instructions indentes constituent ce
que nous appellerons dsormais un bloc dinstructions. Un bloc dinstructions est une suite dinstructions formant un ensemble logique, qui nest excut que dans certaines conditions dfinies dans la
ligne den-tte. Dans lexemple du paragraphe prcdent, les deux lignes dinstructions indentes sous
la ligne contenant linstruction if constituent un mme bloc logique : ces deux lignes ne sont
excutes toutes les deux que si la condition teste avec linstruction if se rvle vraie, cest--dire
si le reste de la division de a par 2 est nul.

Instructions imbriques
Il est parfaitement possible dimbriquer les unes dans les autres plusieurs instructions composes, de
manire raliser des structures de dcision complexes. Exemple :
if embranchement == "vertbrs":
if classe == "mammifres":
if ordre == "carnivores":
if famille == "flins":
print("cest peut-tre un chat")
print("cest en tous cas un mammifre")
elif classe == "oiseaux":
print("cest peut-tre un canari")
print("la classification des animaux est complexe")

#
#
#
#
#
#
#
#
#

1
2
3
4
5
6
7
8
9

Analysez cet exemple. Ce fragment de programme nimprime la phrase cest peut-tre un chat que
dans le cas o les quatre premires conditions testes sont vraies.
Pour que la phrase cest en tous cas un mammifre soit affiche, il faut et il suffit que les deux
premires conditions soient vraies. Linstruction daffichage de cette phrase (ligne 4) se trouve en effet
au mme niveau dindentation que linstruction : if ordre == "carnivores": (ligne 3). Les deux font
donc partie dun mme bloc, lequel est entirement excut si les conditions testes aux lignes 1 et 2
sont vraies.
Pour que la phrase cest peut-tre un canari soit affiche, il faut que la variable
contienne vertbrs , et que la variable classe contienne oiseaux .

embranchement

Quant la phrase de la ligne 9, elle est affiche dans tous les cas, parce quelle fait partie du mme bloc
dinstructions que la ligne 1.

Quelques rgles de syntaxe Python


Tout ce qui prcde nous amne faire le point sur quelques rgles de syntaxe :

Quelques rgles de syntaxe Python

25

Les limites des instructions et des blocs sont dfinies par la mise en page
Dans de nombreux langages de programmation, il faut terminer chaque ligne dinstructions par un
caractre spcial (souvent le point-virgule). Sous Python, cest le caractre de fin de ligne 13 qui joue ce
rle. (Nous verrons plus loin comment outrepasser cette rgle pour tendre une instruction complexe
sur plusieurs lignes.) On peut galement terminer une ligne dinstructions par un commentaire. Un
commentaire Python commence toujours par le caractre spcial #. Tout ce qui est inclus entre ce
caractre et le saut la ligne suivant est compltement ignor par le compilateur.
Dans la plupart des autres langages, un bloc dinstructions doit tre dlimit par des symboles
spcifiques (parfois mme par des instructions, telles que begin et end). En C++ et en Java, par exemple,
un bloc dinstructions doit tre dlimit par des accolades. Cela permet dcrire les blocs dinstructions
les uns la suite des autres, sans se proccuper ni dindentation ni de sauts la ligne, mais cela peut
conduire lcriture de programmes confus, difficiles relire pour les pauvres humains que nous
sommes. On conseille donc tous les programmeurs qui utilisent ces langages de se servir aussi des
sauts la ligne et de lindentation pour bien dlimiter visuellement les blocs.
Avec Python, vous devez utiliser les sauts la ligne et lindentation, mais en contrepartie vous navez
pas vous proccuper dautres symboles dlimiteurs de blocs. En dfinitive, Python vous force donc
crire du code lisible, et prendre de bonnes habitudes que vous conserverez lorsque vous utiliserez
dautres langages.

Instruction compose : en-tte, double point, bloc dinstructions indent


Nous aurons de nombreuses occasions dapprofondir le concept de bloc dinstructions et de faire
des exercices ce sujet ds le chapitre suivant.
Le schma ci-contre en rsume le principe.

Les blocs dinstructions sont toujours associs une ligne


den-tte contenant une instruction bien spcifique (if, elif,
else, while, def, etc.) se terminant par un double point.
Les blocs sont dlimits par lindentation : toutes les lignes dun
mme bloc doivent tre indentes exactement de la mme
manire (cest--dire dcales vers la droite dun mme
nombre despaces). Le nombre despaces utiliser pour lindentation est quelconque, mais la plupart des programmeurs
utilisent des multiples de 4.
Notez que le code du bloc le plus externe (bloc 1) ne peut pas
lui-mme tre cart de la marge de gauche (il nest
imbriqu dans rien).

13 Ce

caractre napparat ni lcran, ni sur les listings imprims. Il est cependant bien prsent, un point
tel quil fait mme problme dans certains cas, parce qu il nest pas encod de la mme manire par tous les
systmes dexploitation. Nous en reparlerons plus loin, l occasion de notre tude des fichiers texte (page
114).

26

Contrle du flux dexcution


Important
Vous pouvez aussi indenter laide de tabulations, mais alors vous devrez faire trs
attention ne pas utiliser tantt des espaces, tantt des tabulations pour indenter les
lignes dun mme bloc. En effet, mme si le rsultat parat identique lcran, espaces et
tabulations sont des codes binaires distincts : Python considrera donc que ces lignes
indentes diffremment font partie de blocs diffrents. Il peut en rsulter des erreurs
difficiles dboguer.
En consquence, la plupart des programmeurs prfrent se passer des tabulations. Si
vous utilisez un diteur de texte intelligent , vous avez tout intrt activer son option
Remplacer les tabulations par des espaces .

Les espaces et les commentaires sont normalement ignors


part ceux qui servent lindentation, en dbut de ligne, les espaces placs lintrieur des
instructions et des expressions sont presque toujours ignors (sauf sils font partie dune chane de
caractres). Il en va de mme pour les commentaires : ceux-ci commencent toujours par un caractre
dise (#) et stendent jusqu la fin de la ligne courante.

4
Instructions rptitives

Lune des tches que les machines font le mieux est la rptition sans erreur de tches identiques. Il
existe bien des mthodes pour programmer ces tches rptitives. Nous allons commencer par lune
des plus fondamentales : la boucle de rptition construite autour de linstruction while.

Raffectation
Nous ne lavions pas encore signal explicitement : il est permis de r-affecter une nouvelle valeur
une mme variable, autant de fois quon le souhaite.
Leffet dune r-affectation est de remplacer lancienne valeur dune variable par une nouvelle.
>>>
>>>
320
>>>
>>>
375

altitude = 320
print(altitude)
altitude = 375
print(altitude)

Ceci nous amne attirer une nouvelle fois votre attention sur le fait que le symbole gale utilis sous
Python pour raliser une affectation ne doit en aucun cas tre confondu avec un symbole dgalit tel
quil est compris en mathmatique. Il est tentant dinterprter linstruction altitude = 320 comme une
affirmation dgalit, mais ce nen est pas une !

Premirement, lgalit est commutative, alors que laffectation ne lest pas. Ainsi, en
mathmatique, les critures a = 7 et 7 = a sont quivalentes, alors quune instruction de
programmation telle que 375 = altitude serait illgale.
Deuximement, lgalit est permanente, alors que laffectation peut tre remplace comme nous
venons de le voir. Lorsquen mathmatique, nous affirmons une galit telle que a = b au dbut
dun raisonnement, alors a continue tre gal b durant tout le dveloppement qui suit.
En programmation, une premire instruction daffectation peut rendre gales les valeurs de deux
variables, et une instruction ultrieure en changer ensuite lune ou lautre. Exemple :
>>> a = 5
>>> b = a
>>> b = 2

# a et b contiennent des valeurs gales


# a et b sont maintenant diffrentes

Rappelons ici que Python permet daffecter leurs valeurs plusieurs variables simultanment :

28

Instructions rptitives

>>> a, b, c, d = 3, 4, 5, 7

Cette fonctionnalit de Python est bien plus intressante encore quelle nen a lair premire vue.
Supposons par exemple que nous voulions maintenant changer les valeurs des variables a et c
(actuellement, a contient la valeur 3, et c la valeur 5 ; nous voudrions que ce soit linverse). Comment
faire ?
Exercice
4.1 crivez les lignes dinstructions ncessaires pour obtenir ce rsultat.

la suite de lexercice propos ci-dessus, vous aurez certainement trouv une mthode, et un
professeur vous demanderait certainement de la commenter en classe. Comme il sagit dune
opration courante, les langages de programmation proposent souvent des raccourcis pour leffectuer
(par exemple des instructions spcialises, telle linstruction SWAP du langage Basic). Sous Python,
laffectation parallle permet de programmer lchange dune manire particulirement lgante :
>>> a, b = b, a

(On pourrait bien entendu changer dautres variables en mme temps, dans la mme instruction.)

Rptitions en boucle Linstruction while


En programmation, on appelle boucle un systme dinstructions qui permet de rpter un certain
nombre de fois (voire indfiniment) toute une srie doprations. Python propose deux instructions
particulires pour construire des boucles : linstruction for in , trs puissante, que nous
tudierons au chapitre 10, et linstruction while que nous allons dcouvrir tout de suite.
Veuillez donc entrer les commandes ci-dessous :
>>> a = 0
>>> while (a < 7):
...
a = a + 1
...
print(a)

# (noubliez pas le double point !)


# (noubliez pas lindentation !)

Frappez encore une fois <Enter>.


Que se passe-t-il ?
Avant de lire les commentaires de la page suivante, prenez le temps douvrir votre cahier et dy noter
cette srie de commandes. Dcrivez aussi le rsultat obtenu, et essayez de lexpliquer de la manire la
plus dtaille possible.

Commentaires
Le mot while signifie tant que en anglais. Cette instruction utilise la seconde ligne indique
Python quil lui faut rpter continuellement le bloc dinstructions qui suit, tant que le contenu de la variable
a reste infrieur 7.
Comme linstruction if aborde au chapitre prcdent, linstruction while amorce une instruction compose. Le double point la fin de la ligne introduit le bloc dinstructions rpter, lequel doit
obligatoirement se trouver en retrait. Comme vous lavez appris au chapitre prcdent, toutes les ins-

Rptitions en boucle Linstruction while

29

tructions dun mme bloc doivent tre indentes exactement au mme niveau (cest--dire dcales
droite dun mme nombre despaces).
Nous avons ainsi construit notre premire boucle de programmation, laquelle rpte un certain nombre
de fois le bloc dinstructions indentes. Voici comment cela fonctionne :

Avec linstruction while, Python commence par valuer la validit de la condition fournie entre
parenthses (celles-ci sont optionnelles, nous ne les avons utilises que pour clarifier notre
explication).
Si la condition se rvle fausse, alors tout le bloc qui suit est ignor et lexcution du programme
se termine14.
Si la codition est vraie, alors Python excute tout le bloc dinstructions constituant le corps de la
boucle, cest--dire :
linstruction a = a + 1 qui incrmente dune unit le contenu de la variable a (ce qui signifie que
lon affecte la variable a une nouvelle valeur, qui est gale la valeur prcdente augmente
dune unit).
lappel de la fonction print() pour afficher la valeur courante de la variable a.
lorsque ces deux instructions ont t excutes, nous avons assist une premire itration, et le
programme boucle, cest--dire que lexcution reprend la ligne contenant linstruction while.
La condition qui sy trouve est nouveau value, et ainsi de suite.
Dans notre exemple, si la condition a < 7 est encore vraie, le corps de la boucle est excut une
nouvelle fois et le bouclage se poursuit.

Remarques
La variable value dans la condition doit exister au pralable (il faut quon lui ait dj affect au
moins une valeur).
Si la condition est fausse au dpart, le corps de la boucle nest jamais excut.
Si la condition reste toujours vraie, alors le corps de la boucle est rpt indfiniment (tout au
moins tant que Python lui-mme continue fonctionner). Il faut donc veiller ce que le corps de
la boucle contienne au moins une instruction qui change la valeur dune variable intervenant dans
la condition value par while, de manire ce que cette condition puisse devenir fausse et la
boucle se terminer.
Exemple de boucle sans fin ( viter !) :
>>> n = 3
>>> while n < 5:
...
print("hello !")

laboration de tables
Recommencez prsent le premier exercice, mais avec la petite modification ci-dessous :
14 ...

du moins dans cet exemple. Nous verrons un peu plus loin qu en fait lexcution continue avec la
premire instruction qui suit le bloc indent, et qui fait partie du mme bloc que l instruction while
elle-mme.

30

Instructions rptitives

>>> a = 0
>>> while a < 12:
...
a = a +1
...
print(a , a**2 , a**3)

Vous devriez obtenir la liste des carrs et des cubes des nombres de 1 12.
Notez au passage que la fonction print() permet dafficher plusieurs expressions lune la suite de
lautre sur la mme ligne : il suffit de les sparer par des virgules. Python insre automatiquement un
espace entre les lments affichs.

Construction dune suite mathmatique


Le petit programme ci-dessous permet dafficher les dix premiers termes dune suite appele Suite de
Fibonacci . Il sagit dune suite de nombres dont chaque terme est gal la somme des deux termes qui
le prcdent. Analysez ce programme (qui utilise judicieusement laffectation parallle) et dcrivez le
mieux possible le rle de chacune des instructions.
>>> a, b, c = 1, 1, 1
>>> while c < 11 :
...
print(b, end =" ")
...
a, b, c = b, a+b, c+1

Lorsque vous lancez lexcution de ce programme, vous obtenez :


1 2 3 5 8 13 21 34 55 89

Les termes de la suite de Fibonacci sont affichs sur la mme ligne. Vous obtenez ce rsultat grce au
second argument end =" " fourni la fonction print(). Par dfaut, la fonction print() ajoute en effet
un caractre de saut la ligne toute valeur quon lui demande dafficher. Largument end =" " signifie que vous souhaitez remplacer le saut la ligne par un simple espace. Si vous supprimez cet
argument, les nombres seront affichs les uns en-dessous des autres.
Dans vos programmes futurs, vous serez trs souvent amens mettre au point des boucles de
rptition comme celle que nous analysons ici. Il sagit dune question essentielle, que vous devez
apprendre matriser parfaitement. Soyez sr que vous y arriverez progressivement, force dexercices.
Lorsque vous examinez un problme de cette nature, vous devez considrer les lignes dinstruction,
bien entendu, mais surtout dcortiquer les tats successifs des diffrentes variables impliques dans la
boucle. Cela nest pas toujours facile, loin de l. Pour vous aider y voir plus clair, prenez la peine de
dessiner sur papier une table dtats similaire celle que nous reproduisons ci-dessous pour notre
programme suite de Fibonacci :

Rptitions en boucle Linstruction while

31

Variables

Valeurs initiales

1
Valeurs prises
successivement, au
cours des itrations 2
3

...

...

...

a+b

c+1

Expression de
remplacement

Dans une telle table, on effectue en quelque sorte la main le travail de lordinateur, en indiquant
ligne par ligne les valeurs que prendront chacune des variables au fur et mesure des itrations
successives. On commence par inscrire en haut du tableau les noms des variables concernes. Sur la
ligne suivante, les valeurs initiales de ces variables (valeurs quelles possdent avant le dmarrage de
la boucle). Enfin, tout en bas du tableau, les expressions utilises dans la boucle pour modifier ltat de
chaque variable chaque itration.
On remplit alors quelques lignes correspondant aux premires itrations. Pour tablir les valeurs
dune ligne, il suffit dappliquer celles de la ligne prcdente, lexpression de remplacement qui se
trouve en bas de chaque colonne. On vrifie ainsi que lon obtient bien la suite recherche. Si ce nest
pas le cas, il faut essayer dautres expressions de remplacement.
Exercices
4.2 crivez un programme qui affiche les 20 premiers termes de la table de multiplication par 7.

4.3

crivez un programme qui affiche une table de conversion de sommes dargent exprimes en
euros, en dollars canadiens. La progression des sommes de la table sera gomtrique ,
comme dans lexemple ci-dessous :
1
2
4
8

euro(s)
euro(s)
euro(s)
euro(s)

=
=
=
=

1.65 dollar(s)
3.30 dollar(s)
6.60 dollar(s)
13.20 dollar(s)

etc. (Sarrter 16384 euros.)


4.4

crivez un programme qui affiche une suite de 12 nombres dont chaque terme soit gal au
triple du terme prcdent.

Premiers scripts, ou comment conserver nos programmes


Jusqu prsent, vous avez toujours utilis Python en mode interactif (cest--dire que vous avez
chaque fois entr les commandes directement dans linterprteur, sans les sauvegarder au pralable
dans un fichier). Cela vous a permis dapprendre trs rapidement les bases du langage, par
exprimentation directe. Cette faon de faire prsente toutefois un gros inconvnient : toutes les
squences dinstructions que vous avez crites disparaissent irrmdiablement ds que vous fermez
linterprteur. Avant de poursuivre plus avant votre tude, il est donc temps que vous appreniez
sauvegarder vos programmes dans des fichiers, sur disque dur ou clef USB, de manire pouvoir les
retravailler par tapes successives, les transfrer sur dautres machines, etc.

32

Instructions rptitives

Pour ce faire, vous allez dsormais rdiger vos squences dinstructions dans un diteur de texte
quelconque (par exemple Kate, Gedit, Geany... sous Linux, Wordpad, Geany, Komodo editor... sous Windows,
ou encore lditeur incorpor dans linterface de dveloppement IDLE qui fait partie de la distribution
de Python pour Windows). Ainsi vous crirez un script, que vous pourrez ensuite sauvegarder,
modifier, copier, etc. comme nimporte quel autre texte trait par ordinateur15.
Par la suite, lorsque vous voudrez tester lexcution de votre programme, il vous suffira de lancer linterprteur Python en lui fournissant (comme argument) le nom du fichier qui contient le script. Par
exemple, si vous avez plac un script dans un fichier nomm MonScript , il suffira dentrer la
commande suivante dans une fentre de terminal pour que ce script sexcute :
python3 MonScript

16

Pour faire mieux encore, veillez choisir pour votre fichier un nom qui se termine par lextension .py
Si vous respectez cette convention, vous pourrez aussi lancer lexcution du script, simplement en
cliquant sur son nom ou sur licne correspondante dans le gestionnaire de fichiers (cest--dire
lexplorateur, sous Windows, ou bien Nautilus, Konqueror... sous Linux).
Ces gestionnaires graphiques savent en effet quils doivent lancer linterprteur Python chaque
fois que leur utilisateur essaye douvrir un fichier dont le nom se termine par .py (cela suppose bien
entendu quils aient t correctement configurs). La mme convention permet en outre aux diteurs
intelligents de reconnatre automatiquement les scripts Python, et dadapter leur coloration
syntaxique en consquence.
La figure ci-aprs illustre lutilisation de lditeur Gedit sous Linux (Ubuntu) pour crire un script :

15 Il

serait parfaitement possible d utiliser un systme de traitement de texte, la condition d effectuer la


sauvegarde sous un format texte pur (sans balises de mise en page). Il est cependant prfrable d utiliser
un vritable diteur intelligent tel que Gedit, Geany, ou IDLE, muni dune fonction de coloration syntaxique
pour Python, qui vous aide viter les fautes de syntaxe. Avec IDLE, suivez le menu : File New window (ou
tapez <Ctrl-N>) pour ouvrir une nouvelle fentre dans laquelle vous crirez votre script. Pour l excuter, il
vous suffira (aprs sauvegarde), de suivre le menu : Edit Run script (ou de taper <Ctrl-F5>).
16 Si linterprteur Python 3 a t install sur votre machine comme interprteur Python par dfaut, vous
devriez pouvoir aussi entrer tout simplement : python MonScript . Mais attention : si plusieurs versions de
Python sont prsentes, il se peut que cette commande active plutt une version antrieure (Python 2.x).

Premiers scripts, ou comment conserver nos programmes

33

Un script Python contiendra des squences dinstructions identiques celles que vous avez
exprimentes jusqu prsent. Puisque ces squences sont destines tre conserves et relues plus
tard par vous-mme ou par dautres, il vous est trs fortement recommand dexpliciter vos scripts le mieux
possible, en y incorporant de nombreux commentaires. La principale difficult de la programmation
consiste en effet mettre au point des algorithmes corrects. Afin que ces algorithmes puissent tre
vrifis, corrigs, modifis, etc. dans de bonnes conditions, il est essentiel que leur auteur les dcrive
le plus compltement et le plus clairement possible. Et le meilleur emplacement pour cette description
est le corps mme du script (ainsi elle ne peut pas sgarer).
Un bon programmeur veille toujours insrer un grand nombre de commentaires dans
ses scripts. En procdant ainsi, non seulement il facilite la comprhension de ses
algorithmes pour dautres lecteurs ventuels, mais encore il se force lui-mme avoir les
ides plus claires.

On peut insrer des commentaires quelconques peu prs nimporte o dans un script. Il suffit de les
faire prcder dun caractre #. Lorsquil rencontre ce caractre, linterprteur Python ignore tout ce
qui suit, jusqu la fin de la ligne courante.
Comprenez bien quil est important dinclure des commentaires au fur et mesure de lavancement de
votre travail de programmation. Nattendez pas que votre script soit termin pour les ajouter aprs
coup . Vous vous rendrez progressivement compte quun programmeur passe normment de temps
relire son propre code (pour le modifier, y rechercher des erreurs, etc.). Cette relecture sera
grandement facilite si le code comporte de nombreuses explications et remarques.
Ouvrez donc un diteur de texte, et rdigez le script ci-dessous :
# Premier essai de script Python
# petit programme simple affichant une suite de Fibonacci, c..d. une suite
# de nombres dont chaque terme est gal la somme des deux prcdents.
a, b, c = 1, 1, 1
print(b)
while c<15:
a, b, c = b, a+b, c+1
print(b)

#
#
#
#

a & b servent au calcul des termes successifs


c est un simple compteur
affichage du premier terme
nous afficherons 15 termes au total

Afin de vous montrer tout de suite le bon exemple, nous commenons ce script par trois lignes de
commentaires, qui contiennent une courte description de la fonctionnalit du programme. Prenez
lhabitude de faire de mme dans vos propres scripts.
Certaines lignes de code sont galement documentes. Si vous procdez comme nous lavons fait,
cest--dire en insrant des commentaires la droite des instructions correspondantes, veillez les
carter suffisamment de celles-ci, afin de ne pas gner leur lisibilit.
Lorsque vous avez bien vrifi votre texte, sauvegardez-le et excutez-le.
Bien que ce ne soit pas indispensable, nous vous recommandons une fois encore de
choisir pour vos scripts des noms de fichiers se terminant par lextension .py. Cela aide
beaucoup les identifier comme tels dans un rpertoire. Les gestionnaires graphiques de
fichiers (explorateur Windows, Nautilus, Konqueror) se servent dailleurs de cette
extension pour leur associer une icne spcifique. vitez cependant de choisir des noms

34

Instructions rptitives
qui risqueraient dtre dj attribus des modules python existants : des noms tels que
math.py ou tkinter.py, par exemple, sont proscrire !

Si vous travaillez en mode texte sous Linux, ou dans une fentre MS-DOS, vous pouvez excuter votre
script laide de la commande python3 suivie du nom du script. Si vous travaillez en mode graphique
sous Linux, vous pouvez ouvrir une fentre de terminal et faire la mme chose :
python3 monScript.py

Dans un gestionnaire graphique de fichiers, vous pouvez en principe lancer lexcution de votre script
en (double) cliquant sur licne correspondante. Ceci ne pourra cependant fonctionner que si cest
bien Python 3 qui a t dsign comme interprteur par dfaut pour les fichiers comportant
lextension .py (des problmes peuvent en effet apparatre si plusieurs versions de Python sont
installes sur votre machine voyez votre professeur ou votre gourou local pour dtailler ces
questions).
Si vous travaillez avec IDLE, vous pouvez lancer lexcution du script en cours ddition, directement
laide de la combinaison de touches <Ctrl-F5>. Dans dautres environnements de travail spcifiquement
ddis Python, tels que Geany, vous trouverez galement des icnes et/ou des raccourcis clavier pour
lancer lexcution (il sagit souvent de la touche <F5>). Consultez votre professeur ou votre gourou
local concernant les autres possibilits de lancement ventuelles sur diffrents systmes dexploitation.

Problmes ventuels lis aux caractres accentus


Si vous avez rdig votre script avec un diteur rcent (tels ceux que nous avons dj indiqus), le
script dcrit ci-dessus devrait sexcuter sans problme avec la version actuelle de Python 3. Si votre
logiciel est ancien ou mal configur, il se peut que vous obteniez un message derreur similaire
celui-ci :
File "fibo2.py", line 2
SyntaxError: Non-UTF-8 code starting with '\xe0' in file fibo2.py on line 2, but no
encoding declared; see http://python.org/dev/peps/pep-0263/ for details

Ce message vous indique que le script contient des caractres typographiques encods suivant une
norme ancienne (vraisemblablement la norme ISO-8859-1 ou Latin-1).
Nous dtaillerons les diffrentes normes dencodage plus loin dans ce livre. Pour linstant, il vous suffit
de savoir que vous devez dans ce cas :

Soit reconfigurer votre diteur de textes pour quil encode les caractres en Utf-8 (ou vous
procurer un autre diteur fonctionnant suivant cette norme). Cest la meilleure solution, car ainsi
vous serez certain lavenir de travailler en accord avec les conventions de standardisation
actuelles, qui finiront tt ou tard par remplacer partout les anciennes.
Soit inclure le pseudo-commentaire suivant au dbut de tous vos scripts (obligatoirement la 1 e
ou la 2e ligne) :
# -*- coding:Latin-1 -*-

Le pseudo-commentaire ci-dessus indique Python que vous utilisez dans votre script le jeu de
caractres accentus ASCII tendu correspondant aux principales langues de lEurope occidentale

Premiers scripts, ou comment conserver nos programmes

35

(franais, allemand, portugais, etc.), encod sur un seul octet suivant la norme ISO-8859-1, laquelle
est souvent dsigne aussi par ltiquette Latin-1 .
Python peut traiter correctement les caractres encods suivant toute une srie de normes. Il faut
donc lui signaler celle que vous utilisez laide dun pseudo-commentaire en dbut de script. Sans
cette indication, Python considre que vos scripts ont t encods en Utf-8 17, suivant la nouvelle
norme Unicode, qui a t mise au point pour standardiser la reprsentation numrique de tous les
caractres spcifiques des diffrentes langues mondiales, ainsi que les symboles mathmatiques,
scientifiques, etc. Il existe plusieurs reprsentations ou encodages de cette norme, nous approfondirons
cette question plus loin18. Pour le moment, il vous suffit de savoir que lencodage le plus rpandu sur
les ordinateurs rcents est Utf-8. Dans ce systme, les caractres standard (ASCII) sont encore encods
sur un seul octet, ce qui assure une certaine compatibilit avec lancienne norme dencodage Latin-1,
mais les autres caractres (parmi lesquels nos caractres accentus) peuvent tre encods sur 2, 3, ou
mme parfois 4 octets.
Nous apprendrons comment grer et convertir ces diffrents encodages, lorsque nous tudierons plus
en dtail le traitement des fichiers texte (au chapitre 9).
Exercices
4.5 crivez un programme qui calcule le volume dun paralllpipde rectangle dont sont fournis
au dpart la largeur, la hauteur et la profondeur.

4.6

crivez un programme qui convertit un nombre entier de secondes fourni au dpart en un


nombre dannes, de mois, de jours, de minutes et de secondes (utilisez loprateur modulo : %
).

4.7

crivez un programme qui affiche les 20 premiers termes de la table de multiplication par 7, en
signalant au passage ( laide dune astrisque) ceux qui sont des multiples de 3.
Exemple : 7 14 21 * 28 35 42 * 49 ...

4.8

crivez un programme qui calcule les 50 premiers termes de la table de multiplication par 13,
mais naffiche que ceux qui sont des multiples de 7.

4.9

crivez un programme qui affiche la suite de symboles suivante :


*
**
***
****
*****
******
*******

17 Dans

les versions de Python antrieures la version 3.0, lencodage par dfaut tait ASCII. Il fallait donc
toujours prciser en dbut de script les autres encodages (y compris Utf-8).
18 Voir page 127

5
Principaux types de donnes

Dans le chapitre 2, nous avons dj manipul des donnes de diffrents types : des nombres entiers
ou rels, et des chanes de caractres. Il est temps prsent dexaminer dun peu plus prs ces types
de donnes, et galement de vous en faire dcouvrir dautres.

Les donnes numriques


Dans les exercices raliss jusqu prsent, nous avons dj utilis des donnes de deux types : les
nombres entiers ordinaires et les nombres rels (aussi appels nombres virgule flottante). Tchons de
mettre en vidence les caractristiques (et les limites) de ces concepts.

Le type integer
Supposons que nous voulions modifier lgrement notre prcdent exercice sur la suite de Fibonacci,
de manire obtenir laffichage dun plus grand nombre de termes. A priori, il suffit de modifier la
condition de bouclage, dans la deuxime ligne. Avec while c <50:, nous devrions obtenir
quarante-neuf termes. Modifions donc lgrement lexercice, de manire afficher aussi le type de la
variable principale :
>>> a, b, c = 1, 1, 1
>>> while c <50:
print(c, ":", b, type(b))
a, b, c = b, a+b, c+1
...
...
... (affichage des 43 premiers termes)
...
44 : 1134903170 <class 'int'>
45 : 1836311903 <class 'int'>
46 : 2971215073 <class 'int'>
47 : 4807526976 <class 'int'>
48 : 7778742049 <class 'int'>
49 : 12586269025 <class 'int'>

Que pouvons-nous constater ? Il semble que Python soit capable de traiter des nombres entiers de
taille illimite. La fonction type() nous permet de vrifier chaque itration que le type de la variable
b reste bien en permanence de ce type.

38

Principaux types de donnes

Lexercice que nous venons de raliser pourrait cependant intriguer ceux dentre vous qui
sinterrogent sur la reprsentation interne des nombres dans un ordinateur. Vous savez probablement
en effet que le cur de celui-ci est constitu par un circuit intgr lectronique (une puce de silicium)
trs haut degr dintgration, qui peut effectuer plus dun milliard doprations en une seule
seconde, mais seulement sur des nombres binaires de taille limite : 32 bits actuellement19. Or, la
gamme de valeurs dcimales quil est possible dencoder sous forme de nombres binaires de 32 bits
stend de -2147483648 +2147483647.
Les oprations effectues sur des entiers compris entre ces deux limites sont donc toujours trs
rapides, parce que le processeur est capable de les traiter directement. En revanche, lorsquil est
question de traiter des nombres entiers plus grands, ou encore des nombres rels (nombres virgule
flottante ), les logiciels que sont les interprteurs et compilateurs doivent effectuer un gros travail de
codage/dcodage, afin de ne prsenter en dfinitive au processeur que des oprations binaires sur des
nombres entiers, de 32 bits au maximum.
Vous navez pas vous proccuper de ces considrations techniques. Lorsque vous lui demandez de
traiter des entiers quelconques, Python les transmet au processeur sous la forme de nombres binaires
de 32 bits chaque fois que cest possible, afin doptimiser la vitesse de calcul et dconomiser lespace
mmoire. Lorsque les valeurs traiter sont des nombres entiers se situant au-del des limites
indiques plus haut, leur encodage dans la mmoire de lordinateur devient plus complexe, et leur
traitement par le processeur ncessite alors plusieurs oprations successives. Tout cela se fait
automatiquement, sans que vous nayez vous en soucier20.
Vous pouvez donc effectuer avec Python des calculs impliquant des valeurs entires comportant un
nombre de chiffres significatifs quelconque. Ce nombre nest limit en effet que par la taille de la
mmoire disponible sur lordinateur utilis. Il va de soi cependant que les calculs impliquant de trs
grands nombres devront tre dcomposs par linterprteur en calculs multiples sur des nombres plus
simples, ce qui pourra ncessiter un temps de traitement considrable dans certains cas.
Exemple :
>>> a, b, c = 3, 2, 1
>>> while c < 15:
print(c, ": ", b)
a, b, c = b, a*b, c+1
1 :
2 :
3 :
4 :
5 :
6 :
7 :
8 :
9 :
10 :
11 :
12 :

2
6
12
72
864
62208
53747712
3343537668096
179707499645975396352
600858794305667322270155425185792
107978831564966913814384922944738457859243070439030784
64880030544660752790736837369104977695001034284228042891827649456186234
19 La

plupart des ordinateurs de bureau actuels contiennent un microprocesseur registres de 32 bits (mme
sil sagit dun modle dual core. Les processeurs 64 bits seront cependant bientt monnaie courante.
20 Les prcdentes versions de Python disposaient de deux types dentiers : integer et long integer, mais la
conversion entre ces deux types est devenue automatique ds la version 2.2.

Les donnes numriques

39

582611607420928
13 : 70056698901118320029237641399576216921624545057972697917383692313271754
88362123506443467340026896520469610300883250624900843742470237847552
14 : 45452807645626579985636294048249351205168239870722946151401655655658398
64222761633581512382578246019698020614153674711609417355051422794795300591700
96950422693079038247634055829175296831946224503933501754776033004012758368256
>>>

Dans lexemple ci-dessus, la valeur des nombres affichs augmente trs rapidement, car chacun deux
est gal au produit des deux termes prcdents. Bien videmment, vous pouvez continuer cette suite
mathmatique si vous voulez. La progression continue avec des nombres gigantesques, mais la vitesse
de calcul diminue au fur et mesure.
Les entiers de valeur comprise entre les deux limites indiques plus haut occupent
chacun 32 bits dans la mmoire de lordinateur. Les trs grands entiers occupent une
place variable, en fonction de leur taille.

Le type float
Vous avez dj rencontr prcdemment cet autre type de donne numrique : le type nombre
rel , ou nombre virgule flottante , dsign en anglais par lexpression floating point number, et
que pour cette raison on appellera type float sous Python.
Ce type autorise les calculs sur de trs grands ou trs petits nombres (donnes scientifiques, par
exemple), avec un degr de prcision constant.
Pour quune donne numrique soit considre par Python comme tant du type float, il suffit quelle
contienne dans sa formulation un lment tel quun point dcimal ou un exposant de 10.
Par exemple, les donnes :
3.14

10.

.001

1e100

3.14e-10

sont automatiquement interprtes par Python comme tant du type float.


Essayons donc ce type de donnes dans un nouveau petit programme (inspir du prcdent) :
>>> a, b, c = 1., 2., 1
>>> while c <18:
...
a, b, c = b, b*a, c+1
...
print(b)
2.0
4.0
8.0
32.0
256.0
8192.0
2097152.0
17179869184.0
3.6028797019e+16
6.18970019643e+26
2.23007451985e+43
1.38034926936e+70
3.07828173409e+113
4.24910394253e+183
1.30799390526e+297

# => a et b seront du type 'float'

40

Principaux types de donnes


Inf
Inf

Comme vous laurez certainement bien compris, nous affichons cette fois encore une srie dont les
termes augmentent extrmement vite, chacun deux tant gal au produit des deux prcdents. Au
neuvime terme, Python passe automatiquement la notation scientifique ( e+n signifie en fait :
fois dix lexposant n ). Aprs le quinzime terme, nous assistons nouveau un dpassement de
capacit (sans message derreur) : les nombres vraiment trop grands sont tout simplement nots
inf (pour infini ).
Le type float utilis dans notre exemple permet de manipuler des nombres (positifs ou ngatifs)
compris entre 10-308 et 10308 avec une prcision de 12 chiffres significatifs. Ces nombres sont encods
dune manire particulire sur 8 octets (64 bits) dans la mmoire de la machine : une partie du code
correspond aux 12 chiffres significatifs, et une autre lordre de grandeur (exposant de 10).
Exercices
5.1 crivez un programme qui convertisse en radians un angle fourni au dpart en degrs,
minutes, secondes.

5.2

crivez un programme qui convertisse en degrs, minutes, secondes un angle fourni au dpart
en radians.

5.3

crivez un programme qui convertisse en degrs Celsius une temprature exprime au dpart
en degrs Fahrenheit, ou linverse.
La formule de conversion est : T F =T C 1,832 .

5.4

crivez un programme qui calcule les intrts accumuls chaque anne pendant 20 ans, par
capitalisation dune somme de 100 euros place en banque au taux fixe de 4,3 %

5.5

Une lgende de lInde ancienne raconte que le jeu dchecs a t invent par un vieux sage,
que son roi voulut remercier en lui affirmant quil lui accorderait nimporte quel cadeau en
rcompense. Le vieux sage demanda quon lui fournisse simplement un peu de riz pour ses
vieux jours, et plus prcisment un nombre de grains de riz suffisant pour que lon puisse en
dposer 1 seul sur la premire case du jeu quil venait dinventer, deux sur la suivante, quatre
sur la troisime, et ainsi de suite jusqu la 64e case.
crivez un programme Python qui affiche le nombre de grains dposer sur chacune des 64
cases du jeu. Calculez ce nombre de deux manires :
le nombre exact de grains (nombre entier) ;
le nombre de grains en notation scientifique (nombre rel).

Les donnes alphanumriques


Jusqu prsent nous navons manipul que des nombres. Mais un programme dordinateur peut
galement traiter des caractres alphabtiques, des mots, des phrases, ou des suites de symboles
quelconques. Dans la plupart des langages de programmation, il existe pour cet usage des structures
de donnes particulires que lon appelle chanes de caractres .

Les donnes alphanumriques

41

Nous apprendrons au chapitre 10 quil ne faut pas confondre les notions de chane de
caractres et squence doctets comme le faisaient abusivement les langages de
programmation anciens (dont les premires versions de Python). Pour linstant,
rjouissons-nous que Python traite dsormais de manire parfaitement cohrente toutes
les chanes de caractres, ceux-ci pouvant faire partie dalphabets quelconques 21.

Le type string
Une donne de type string peut se dfinir en premire approximation comme une suite quelconque de
caractres. Dans un script python, on peut dlimiter une telle suite de caractres, soit par des
apostrophes (simple quotes), soit par des guillemets (double quotes). Exemples :
>>> phrase1 = 'les oeufs durs.'
>>> phrase2 = '"Oui", rpondit-il,'
>>> phrase3 = "j'aime bien"
>>> print(phrase2, phrase3, phrase1)
"Oui", rpondit-il, j'aime bien les oeufs durs.

Les 3 variables phrase1, phrase2, phrase3 sont donc des variables de type string.
Remarquez lutilisation des guillemets pour dlimiter une chane dans laquelle il y a des apostrophes,
ou lutilisation des apostrophes pour dlimiter une chane qui contient des guillemets. Remarquez
aussi encore une fois que la fonction print() insre un espace entre les lments affichs.
Le caractre spcial \ (antislash) permet quelques subtilits complmentaires :

En premier lieu, il permet dcrire sur plusieurs lignes une commande qui serait trop longue pour
tenir sur une seule (cela vaut pour nimporte quel type de commande).
lintrieur dune chane de caractres, lantislash permet dinsrer un certain nombre de codes
spciaux (sauts la ligne, apostrophes, guillemets, etc.). Exemples :
>>> txt3 = '"N\'est-ce pas ?" rpondit-elle.'
>>> print(txt3)
"N'est-ce pas ?" rpondit-elle.
>>> Salut = "Ceci est une chane plutt longue\n contenant plusieurs lignes \
... de texte (Ceci fonctionne\n de la mme faon en C/C++.\n\
...
Notez que les blancs en dbut\n de ligne sont significatifs.\n"
>>> print(Salut)
Ceci est une chane plutt longue
contenant plusieurs lignes de texte (Ceci fonctionne
de la mme faon en C/C++.
Notez que les blancs en dbut
de ligne sont significatifs.

21 Ceci

constitue donc lune des grandes nouveauts de la version 3 de Python par rapport aux versions
prcdentes. Dans celles-ci, une donne de type string tait en ralit une squence doctets et non une squence
de caractres. Cela ne posait gure de problmes pour traiter des textes contenant seulement les caractres
principaux des langues dEurope occidentale, car il tait possible dencoder chacun de ces caractres sur un
seul octet (en suivant par exemple la norme Latin-1). Cela entranait cependant de grosses difficults si lon
voulait rassembler dans un mme texte des caractres tirs dalphabets diffrents, ou simplement utiliser des
alphabets comportant plus de 256 caractres, des symboles mathmatiques particuliers, etc. Vous trouverez
davantage dinformations ce sujet au chapitre 10.

42

Principaux types de donnes

Remarques

La squence \n dans une chane provoque un saut la ligne.


La squence \' permet dinsrer une apostrophe dans une chane dlimite par des apostrophes.
De la mme manire, la squence \" permet dinsrer des guillemets dans une chane dlimite
elle-mme par des guillemets.
Rappelons encore ici que la casse est significative dans les noms de variables (il faut respecter
scrupuleusement le choix initial de majuscules ou minuscules).
Triple quotes
Pour insrer plus aisment des caractres spciaux ou exotiques dans une chane, sans faire usage
de lantislash, ou pour faire accepter lantislash lui-mme dans la chane, on peut encore dlimiter la
chane laide de triples guillemets ou de triples apostrophes :
>>> a1 = """
...
Exemple de texte prformat, c'est--dire
...
dont les indentations et les
...
caractres spciaux \ ' " sont
... conservs sans
...
autre forme de procs."""
>>> print(a1)
Exemple de texte prformat, c'est--dire
dont les indentations et les
caractres spciaux \ ' " sont
conservs sans
autre forme de procs.
>>>

Accs aux caractres individuels dune chane


Les chanes de caractres constituent un cas particulier dun type de donnes plus gnral que lon
appelle des donnes composites. Une donne composite est une entit qui rassemble dans une seule
structure un ensemble dentits plus simples : dans le cas dune chane de caractres, par exemple, ces
entits plus simples sont videmment les caractres eux-mmes. En fonction des circonstances, nous
souhaiterons traiter la chane de caractres, tantt comme un seul objet, tantt comme une collection
de caractres distincts. Un langage de programmation tel que Python doit donc tre pourvu de
mcanismes qui permettent daccder sparment chacun des caractres dune chane. Comme vous
allez le voir, cela nest pas bien compliqu.
Python considre quune chane de caractres est un objet de la catgorie des squences, lesquelles sont
des collections ordonnes dlments. Cela signifie simplement que les caractres dune chane sont
toujours disposs dans un certain ordre. Par consquent, chaque caractre de la chane peut tre
dsign par sa place dans la squence, laide dun index.
Pour accder un caractre bien dtermin, on utilise le nom de la variable qui contient la chane et
on lui accole, entre deux crochets, lindex numrique qui correspond la position du caractre dans la
chane.

Les donnes alphanumriques

43

Attention cependant : comme vous aurez loccasion de le vrifier par ailleurs, les donnes informatiques
sont presque toujours numrotes partir de zro (et non partir de un). Cest le cas pour les caractres
dune chane.
Exemple :
>>> ch = "Christine"
>>> print(ch[0], ch[3], ch[5])
C i t

Vous pouvez recommencer lexercice de lexemple ci-dessus en utilisant cette fois un ou deux
caractres non-ASCII, tels que lettres accentues, cdilles, etc. Contrairement ce qui pouvait se
passer dans certains cas avec les versions de Python antrieures la version 3.0, vous obtenez sans
surprise les rsultats attendus :
>>> ch ="Nol en Dcembre"
>>> print(ch[1],ch[2],ch[3],ch[4],ch[8],ch[9],ch[10],ch[11],ch[12])
o l
D c e m

Ne vous proccupez pas pour linstant de savoir de quelle manire Python mmorise et traite les
caractres typographiques dans la mmoire de lordinateur. Sachez cependant que la technique
utilise exploite la norme internationale Unicode. qui permet de distinguer de faon univoque
nimporte quel caractre de nimporte quel alphabet. Vous pourrez donc mlanger dans une mme
chane des caractres latins, grecs, cyrilliques, arabes... (y compris les caractres accentus), ainsi que
des symboles mathmatiques, des pictogrammes, etc.
Nous verrons au chapitre 10 (voir page 121) comment faire apparatre dautres
caractres que ceux qui sont directement accessibles au clavier.

Oprations lmentaires sur les chanes


Python intgre de nombreuses fonctions qui permettent deffectuer divers traitements sur les chanes
de caractres (conversions majuscules/minuscules, dcoupage en chanes plus petites, recherche de
mots, etc.). Une fois de plus, cependant, nous devons vous demander de patienter : ces questions ne
seront dveloppes qu partir du chapitre 10 (voir page 121).
Pour linstant, nous pouvons nous contenter de savoir quil est possible daccder individuellement
chacun des caractres dune chane, comme cela a t expliqu dans la section prcdente. Sachons en
outre que lon peut aussi :

assembler plusieurs petites chanes pour en construire de plus grandes. Cette opration sappelle
concatnation et on la ralise sous Python laide de loprateur + (cet oprateur ralise donc lopration daddition lorsquon lapplique des nombres, et lopration de concatnation lorsquon
lapplique des chanes de caractres). Exemple :
a = 'Petit poisson'
b = ' deviendra grand'
c = a + b
print(c)
petit poisson deviendra grand

dterminer la longueur (cest--dire le nombre de caractres) dune chane, en faisant appel la


fonction intgre len() :

44

Principaux types de donnes


>>> ch ='Georges'
>>> print(len(ch))
7

Cela marche tout aussi bien si la chane contient des caractres accentus :
>>> ch ='Ren'
>>> print(len(ch))
4

Convertir en nombre vritable une chane de caractres qui reprsente un nombre. Exemple :
>>> ch = '8647'
>>> print(ch + 45)

*** erreur *** : on ne peut pas additionner une chane et un nombre

>>> n = int(ch)
>>> print(n + 65)
8712

# OK : on peut additionner 2 nombres

Dans cet exemple, la fonction intgre int() convertit la chane en nombre entier. Il serait
galement possible de convertir une chane de caractres en nombre rel, laide de la fonction
intgre float().
Exercices
5.6 crivez un script qui dtermine si une chane contient ou non le caractre e .

5.7

crivez un script qui compte le nombre doccurrences du caractre e dans une chane.

5.8

crivez un script qui recopie une chane (dans une nouvelle variable), en insrant des
astrisques entre les caractres.
Ainsi par exemple, gaston devra devenir g*a*s*t*o*n

5.9

crivez un script qui recopie une chane (dans une nouvelle variable) en linversant.
Ainsi par exemple, zorglub deviendra bulgroz .

5.10 En partant de lexercice prcdent, crivez un script qui dtermine si une chane de caractres
donne est un palindrome (cest--dire une chane qui peut se lire indiffremment dans les
deux sens), comme par exemple radar ou s.o.s .

Les listes (premire approche)


Les chanes que nous avons abordes la rubrique prcdente constituaient un premier exemple de
donnes composites. On appelle ainsi les structures de donnes qui sont utilises pour regrouper de
manire structure des ensembles de valeurs. Vous apprendrez progressivement utiliser plusieurs
autres types de donnes composites, parmi lesquelles les listes, les tuples et les dictionnaires22. Nous nallons cependant aborder ici que le premier de ces trois types, et ce de faon assez sommaire. Il s agit-l
en effet dun sujet fort vaste, sur lequel nous devrons revenir plusieurs reprises.
Sous Python, on peut dfinir une liste comme une collection dlments spars par des virgules, lensemble
tant enferm dans des crochets. Exemple :

22

Vous pourrez mme crer vos propres types de donnes composites, lorsque vous aurez assimil le
concept de classe (voir page 163).

Les listes (premire approche)

45

>>> jour = ['lundi', 'mardi', 'mercredi', 1800, 20.357, 'jeudi', 'vendredi']


>>> print(jour)
['lundi', 'mardi', 'mercredi', 1800, 20.357, 'jeudi', 'vendredi']

Dans cet exemple, la valeur de la variable jour est une liste.


Comme on peut le constater dans le mme exemple, les lments individuels qui constituent une liste
peuvent tre de types varis. Dans cet exemple, en effet, les trois premiers lments sont des chanes
de caractres, le quatrime lment est un entier, le cinquime un rel, etc. (nous verrons plus loin
quun lment dune liste peut lui-mme tre une liste !). cet gard, le concept de liste est donc assez
diffrent du concept de tableau (array) ou de variable indice que lon rencontre dans dautres
langages de programmation.
Remarquons aussi que, comme les chanes de caractres, les listes sont des squences, cest--dire des
collections ordonnes dobjets. Les divers lments qui constituent une liste sont en effet toujours
disposs dans le mme ordre, et lon peut donc accder chacun dentre eux individuellement si lon
connat son index dans la liste. Comme ctait dj le cas pour les caractres dans une chane, il faut
cependant retenir que la numrotation de ces index commence partir de zro, et non partir de un.
Exemples :
>>> jour = ['lundi', 'mardi', 'mercredi', 1800, 20.357, 'jeudi', 'vendredi']
>>> print(jour[2])
mercredi
>>> print(jour[4])
20.357

la diffrence de ce qui se passe pour les chanes, qui constituent un type de donnes non-modifiables
(nous aurons plus loin diverses occasions de revenir l-dessus), il est possible de changer les lments
individuels dune liste :
>>> print(jour)
['lundi', 'mardi', 'mercredi', 1800, 20.357, 'jeudi', 'vendredi']
>>> jour[3] = jour[3] +47
>>> print(jour)
['lundi', 'mardi', 'mercredi', 1847, 20.357, 'jeudi', 'vendredi']

On peut donc remplacer certains lments dune liste par dautres, comme ci-dessous :
>>> jour[3] = 'Juillet'
>>> print(jour)
['lundi', 'mardi', 'mercredi', 'Juillet', 20.357, 'jeudi', 'vendredi']

La fonction intgre len(), que nous avons dj rencontre propos des chanes, sapplique aussi aux
listes. Elle renvoie le nombre dlments prsents dans la liste :
>>> print(len(jour))
7

46

Principaux types de donnes

Une autre fonction intgre permet de supprimer dune liste un lment quelconque ( partir de son
index). Il sagit de la fonction del() 23 :
>>> del(jour[4])
>>> print(jour)
['lundi', 'mardi', 'mercredi', 'juillet', 'jeudi', 'vendredi']

Il est galement tout fait possible dajouter un lment une liste, mais pour ce faire, il faut
considrer que la liste est un objet, dont on va utiliser lune des mthodes. Les concepts informatiques
dobjet et de mthode ne seront expliqus quun peu plus loin dans ces notes, mais nous pouvons ds
prsent montrer comment a marche dans le cas particulier dune liste :
>>> jour.append('samedi')
>>> print(jour)
['lundi', 'mardi', 'mercredi', 'juillet', 'jeudi', 'vendredi', 'samedi']
>>>

Dans la premire ligne de lexemple ci-dessus, nous avons appliqu la mthode append() lobjet jour,
avec largument samedi. Si lon se rappelle que le mot append signifie ajouter en anglais, on
peut comprendre que la mthode append() est une sorte de fonction qui est en quelque manire
attache ou intgre aux objets du type liste . Largument que lon utilise avec cette fonction est
bien entendu llment que lon veut ajouter la fin de la liste.
Nous verrons plus loin quil existe ainsi toute une srie de ces mthodes (cest--dire des fonctions
intgres, ou plutt encapsules dans les objets de type liste ). Notons simplement au passage
que lon applique une mthode un objet en reliant les deux laide dun point. (Dabord le nom de la
variable qui rfrence lobjet, puis le point, puis le nom de la mthode, cette dernire toujours
accompagne dune paire de parenthses.)
Comme les chanes de caractres, les listes seront approfondies plus loin dans ces notes (voir page 141
). Nous en savons cependant assez pour commencer les utiliser dans nos programmes. Veuillez par
exemple analyser le petit script ci-dessous et commenter son fonctionnement :
jour = ['dimanche','lundi','mardi','mercredi','jeudi','vendredi','samedi']
a, b = 0, 0
while a<25:
a = a + 1
b = a % 7
print(a, jour[b])

La 5e ligne de cet exemple fait usage de loprateur modulo dj rencontr prcdemment et qui
peut rendre de grands services en programmation. On le reprsente par % dans de nombreux langages
(dont Python). Quelle est lopration effectue par cet oprateur ?

23 Il

existe en fait tout un ensemble de techniques qui permettent de dcouper une liste en tranches, d y
insrer des groupes dlments, den enlever dautres, etc., en utilisant une syntaxe particulire o
ninterviennent que les index.
Cet ensemble de techniques (qui peuvent aussi sappliquer aux chanes de caractres) porte le nom gnrique
de slicing (tranchage). On le met en uvre en plaant plusieurs indices au lieu d un seul entre les crochets que
lon accole au nom de la variable. Ainsi jour[1:3] dsigne le sous-ensemble [ mardi, mercredi].
Ces techniques un peu particulires sont dcrites plus loin (voir pages 121 et suivantes).

Les listes (premire approche)

47

Exercices
5.11 Soient les listes suivantes :
t1 = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
t2 = ['Janvier', 'Fvrier', 'Mars', 'Avril', 'Mai', 'Juin',
'Juillet', 'Aot', 'Septembre', 'Octobre', 'Novembre', 'Dcembre']

crivez un petit programme qui cre une nouvelle liste t3. Celle-ci devra contenir tous les
lments des deux listes en les alternant, de telle manire que chaque nom de mois soit suivi
du nombre de jours correspondant :
['Janvier',31,'Fvrier',28,'Mars',31, etc...].

5.12 crivez un programme qui affiche proprement tous les lments dune liste. Si on lappliquait par exemple la liste t2 de lexercice ci-dessus, on devrait obtenir :
Janvier Fvrier Mars Avril Mai Juin Juillet Aot Septembre Octobre Novembre
Dcembre

5.13 crivez un programme qui recherche le plus grand lment prsent dans une liste donne. Par
exemple, si on lappliquait la liste [32, 5, 12, 8, 3, 75, 2, 15] , ce programme devrait
afficher :
le plus grand lment de cette liste a la valeur 75.

5.14 crivez un programme qui analyse un par un tous les lments dune liste de nombres (par
exemple celle de lexercice prcdent) pour gnrer deux nouvelles listes. Lune contiendra
seulement les nombres pairs de la liste initiale, et lautre les nombres impairs. Par exemple, si la
liste initiale est celle de lexercice prcdent, le programme devra construire une liste pairs
qui contiendra [32, 12, 8, 2], et une liste impairs qui contiendra [5, 3, 75, 15]. Astuce :
pensez utiliser loprateur modulo (%) dj cit prcdemment.
5.15 crivez un programme qui analyse un par un tous les lments dune liste de mots (par
exemple : ['Jean', 'Maximilien', 'Brigitte', 'Sonia', 'Jean-Pierre', 'Sandra']) pour
gnrer deux nouvelles listes. Lune contiendra les mots comportant moins de 6 caractres,
lautre les mots comportant 6 caractres ou davantage.

6
Fonctions prdfinies

Lun des concepts les plus importants en programmation est celui de fonction 24. Les fonctions
permettent en effet de dcomposer un programme complexe en une srie de sous-programmes plus
simples, lesquels peuvent leur tour tre dcomposs en fragments plus petits, et ainsi de suite.
Dautre part, les fonctions sont rutilisables : si nous disposons dune fonction capable de calculer
une racine carre, par exemple, nous pouvons lutiliser un peu partout dans nos programmes sans
avoir la r-crire chaque fois.

La fonction print()
Nous avons bien videmment dj rencontr cette fonction. Prcisons simplement ici quelle permet
dafficher nimporte quel nombre de valeurs fournies en arguments (cest--dire entre les
parenthses). Par dfaut, ces valeurs seront spares les unes des autres par un espace, et le tout se
terminera par un saut la ligne.
Vous pouvez remplacer le sparateur par dfaut (lespace) par un autre caractre quelconque (ou
mme par aucun caractre), grce largument sep. Exemple :
>>> print("Bonjour", "", "tous", sep ="*")
Bonjour**tous
>>> print("Bonjour", "", "tous", sep ="")
Bonjourtous

De mme, vous pouvez remplacer le saut la ligne par largument end :


>>> n =0
>>> while n<6:
...
print("zut", end ="")
...
n = n+1
...
zutzutzutzutzut

24 Sous

Python, le terme fonction est utilis indiffremment pour dsigner la fois de vritables fonctions
mais galement des procdures. Nous indiquerons plus loin la distinction entre ces deux concepts proches.

50

Fonctions prdfinies

Interaction avec lutilisateur : la fonction input()


La plupart des scripts labors ncessitent un moment ou lautre une intervention de lutilisateur
(entre dun paramtre, clic de souris sur un bouton, etc.). Dans un script en mode texte (comme ceux
que nous avons crs jusqu prsent), la mthode la plus simple consiste employer la fonction
intgre input(). Cette fonction provoque une interruption dans le programme courant. Lutilisateur
est invit entrer des caractres au clavier et terminer avec <Enter>. Lorsque cette touche est
enfonce, lexcution du programme se poursuit, et la fonction fournit en retour une chane de
caractres correspondant ce que lutilisateur a saisi. Cette chane peut alors tre assigne une
variable quelconque, convertie, etc.
On peut invoquer la fonction input() en laissant les parenthses vides. On peut aussi y placer en
argument un message explicatif destin lutilisateur. Exemple :
prenom = input("Entrez votre prnom : ")
print("Bonjour,", prenom)

ou encore :
print("Veuillez entrer un nombre positif quelconque : ", end=" ")
ch = input()
nn = int(ch)
# conversion de la chane en un nombre entier
print("Le carr de", nn, "vaut", nn**2)

Soulignons que la fonction input() renvoie toujours une chane de caractres 25. Si vous souhaitez que
lutilisateur entre une valeur numrique, vous devrez donc convertir la valeur entre (qui sera donc de
toute faon de type string) en une valeur numrique du type qui vous convient, par lintermdiaire
des fonctions intgres int() (si vous attendez un entier) ou float() (si vous attendez un rel).
Exemple :
>>> a = input("Entrez une donne numrique : ")
Entrez une donne numrique : 52.37
>>> type(a)
<class 'str'>
>>> b = float(a)
# conversion de la chane en un nombre rel
>>> type(b)
<class 'float'>

Importer un module de fonctions


Vous avez dj rencontr dautres fonctions intgres au langage lui-mme, comme la fonction len(), par
exemple, qui permet de connatre la longueur dune chane de caractres. Il va de soi cependant quil
nest pas possible dintgrer toutes les fonctions imaginables dans le corps standard de Python, car il
en existe virtuellement une infinit : vous apprendrez dailleurs trs bientt comment en crer
vous-mme de nouvelles. Les fonctions intgres au langage sont relativement peu nombreuses : ce
sont seulement celles qui sont susceptibles dtre utilises trs frquemment. Les autres sont
regroupes dans des fichiers spars que lon appelle des modules.
25 Dans

les versions de Python antrieures la version 3.0, la valeur renvoye par input() tait de type
variable, suivant ce que lutilisateur avait saisi. Le comportement actuel est en fait celui de lancienne
fonction raw_input(), que lui prfraient la plupart des programmeurs.

Importer un module de fonctions

51

Les modules sont des fichiers qui regroupent des ensembles de fonctions 26.

Vous verrez plus loin combien il est commode de dcouper un programme important en plusieurs
fichiers de taille modeste pour en faciliter la maintenance. Une application Python typique sera alors
constitue dun programme principal, accompagn de un ou plusieurs modules contenant chacun les
dfinitions dun certain nombre de fonctions accessoires.
Il existe un grand nombre de modules pr-programms qui sont fournis doffice avec Python. Vous
pouvez en trouver dautres chez divers fournisseurs. Souvent on essaie de regrouper dans un mme
module des ensembles de fonctions apparentes, que lon appelle des bibliothques.
Le module math, par exemple, contient les dfinitions de nombreuses fonctions mathmatiques telles
que sinus, cosinus, tangente, racine carre, etc. Pour pouvoir utiliser ces fonctions, il vous suffit dincorporer la ligne suivante au dbut de votre script :
from math import *

Cette ligne indique Python quil lui faut inclure dans le programme courant toutes les fonctions (cest
l la signification du symbole joker * ) du module math, lequel contient une bibliothque de
fonctions mathmatiques pr-programmes.
Dans le corps du script lui-mme, vous crirez par exemple :
racine = sqrt(nombre) pour assigner la variable racine la racine carre de nombre,
sinusx = sin(angle) pour assigner la variable sinusx le sinus de angle (en radians !), etc.
Exemple :
# Dmo : utilisation des fonctions du module <math>
from math import *
nombre = 121
angle = pi/6

# soit 30
# (la bibliothque math inclut aussi la dfinition de pi)
print("racine carre de", nombre, "=", sqrt(nombre))
print("sinus de", angle, "radians", "=", sin(angle))

Lexcution de ce script provoque laffichage suivant :


racine carre de 121 = 11.0
sinus de 0.523598775598 radians = 0.5

Ce court exemple illustre dj fort bien quelques caractristiques importantes des fonctions :

une fonction apparat sous la forme dun nom quelconque associ des parenthses
exemple : sqrt()
dans les parenthses, on transmet la fonction un ou plusieurs arguments
exemple : sqrt(121)
la fonction fournit une valeur de retour (on dira aussi quelle retourne , ou mieux, quelle renvoie une valeur)
exemple : 11.0
26 En

toute rigueur, un module peut contenir aussi des dfinitions de variables ainsi que des classes. Nous
pouvons toutefois laisser ces prcisions de ct, provisoirement.

52

Fonctions prdfinies

Nous allons dvelopper tout ceci dans les pages suivantes. Veuillez noter au passage que les fonctions
mathmatiques utilises ici ne reprsentent quun tout premier exemple. Un simple coup dil dans la
documentation des bibliothques Python vous permettra de constater que de trs nombreuses
fonctions sont dores et dj disponibles pour raliser une multitude de tches, y compris des
algorithmes mathmatiques trs complexes (Python est couramment utilis dans les universits pour
la rsolution de problmes scientifiques de haut niveau). Il est donc hors de question de fournir ici une
liste dtaille. Une telle liste est aisment accessible dans le systme daide de Python :
Documentation HTML Python documentation Modules index math
Au chapitre suivant, nous apprendrons comment crer nous-mmes de nouvelles fonctions.
Exercices
Dans tous ces exercices, utilisez la fonction

input() pour lentre des donnes.

6.1

crivez un programme qui convertisse en mtres par seconde et en km/h une vitesse fournie
par lutilisateur en miles/heure. (Rappel : 1 mile = 1609 mtres)

6.2

crivez un programme qui calcule le primtre et laire dun triangle quelconque dont lutilisateur fournit les 3 cts.
(Rappel : laire dun triangle quelconque se calcule laide de la formule :
S= d d ad bd c

dans laquelle d dsigne la longueur du demi-primtre, et a, b, c celles des trois cts.)


6.3

crivez un programme qui calcule la priode dun pendule simple de longueur donne.

La formule qui permet de calculer la priode dun pendule simple est T =2 l ,


g
l reprsentant la longueur du pendule et g la valeur de lacclration de la pesanteur au lieu
dexprience.
6.4

crivez un programme qui permette dencoder des valeurs dans une liste. Ce programme
devrait fonctionner en boucle, lutilisateur tant invit entrer sans cesse de nouvelles
valeurs, jusqu ce quil dcide de terminer en frappant <Enter> en guise dentre. Le
programme se terminerait alors par laffichage de la liste. Exemple de fonctionnement :
Veuillez
Veuillez
Veuillez
Veuillez
[25, 18,

entrer
entrer
entrer
entrer
6284]

une
une
une
une

valeur
valeur
valeur
valeur

: 25
: 18
: 6284
:

Un peu de dtente avec le module turtle


Comme nous venons de le voir, lune des grandes qualits de Python est quil est extrmement facile
de lui ajouter de nombreuses fonctionnalits par importation de divers modules.
Pour illustrer cela, et nous amuser un peu avec dautres objets que des nombres, nous allons explorer
un module Python qui permet de raliser des graphiques tortue , cest--dire des dessins

Un peu de dtente avec le module turtle

53

gomtriques correspondant la piste laisse derrire elle par une petite tortue virtuelle, dont
nous contrlons les dplacements sur lcran de lordinateur laide dinstructions simples.
Activer cette tortue est un vrai jeu denfant. Plutt que de vous donner de longues explications, nous
vous invitons essayer tout de suite :
>>>
>>>
>>>
>>>
>>>

from turtle import *


forward(120)
left(90)
color('red')
forward(80)

Lexercice est videmment plus riche


si lon utilise des boucles :
>>> reset()
>>> a = 0
>>> while a <12:
a = a +1
forward(150)
left(150)

Attention cependant : avant de lancer


un tel script, assurez-vous toujours
quil ne comporte pas de boucle sans
fin (voir page 29), car si cest le cas
vous risquez de ne plus pouvoir
reprendre le contrle des oprations (en particulier sous Windows).
Amusez-vous crire des scripts qui ralisent des dessins suivant un modle impos lavance. Les
principales fonctions mises votre disposition dans le module turtle sont les suivantes :
reset()
On efface tout et on recommence
goto(x, y)
Aller lendroit de coordonnes x, y
forward(distance) Avancer dune distance donne
backward(distance) Reculer
up()
Relever le crayon (pour pouvoir avancer sans dessiner)
down()
Abaisser le crayon (pour recommencer dessiner)
color(couleur)
couleur peut tre une chane prdfinie (red, blue, etc.)
left(angle)
Tourner gauche dun angle donn (exprim en degrs)
right(angle)
Tourner droite
width(paisseur)
Choisir lpaisseur du trac
fill(1)
Remplir un contour ferm laide de la couleur slectionne
write(texte)
texte doit tre une chane de caractres

Vracit/fausset dune expression


Lorsquun programme contient des instructions telles que while ou if, lordinateur qui excute ce
programme doit valuer la vracit dune condition, cest--dire dterminer si une expression est vraie
ou fausse. Par exemple, une boucle initie par while c<20: sexcutera aussi longtemps que la
condition c<20 restera vraie.

54

Fonctions prdfinies

Mais comment un ordinateur peut-il dterminer si quelque chose est vrai ou faux ?
En fait et vous le savez dj un ordinateur ne manipule strictement que des nombres. Tout ce quun
ordinateur doit traiter doit dabord toujours tre converti en valeur numrique. Cela sapplique aussi
la notion de vrai/faux. En Python, tout comme en C, en Basic et en de nombreux autres langages de programmation, on considre que toute valeur numrique autre que zro est vraie . Seule la valeur
zro est fausse . Exemple :
ch = input('Entrez un nombre entier quelconque')
n =int(ch)
if n:
print("vrai")
else:
print("faux")

Le petit script ci-dessus naffiche


numrique, vous obtiendrez vrai.

faux

que si vous entrez la valeur 0. Pour toute autre valeur

Ce qui prcde signifie donc quune expression valuer, telle par exemple la condition a > 5, est
dabord convertie par lordinateur en une valeur numrique (1 si lexpression est vraie, et zro si
lexpression est fausse). Ce nest pas tout fait vident, parce que linterprteur Python est dot dun
dispositif qui traduit ces deux valeurs en True ou False lorsquon les lui demande explicitement.
Exemple :
>>> a, b = 3, 8
>>> c = (a < b)
>>> d = (a > b)
>>> c
True
>>> d
False

Lexpression a < b est value, et son rsultat (vrai) est mmoris dans la variable c. De mme pour
le rsultat de lexpression inverse, dans la variable d27.
laide dune petite astuce, nous pouvons tout de mme vrifier que ces valeurs
en ralit les deux nombres 1 et 0 dguiss :

True

et

False

sont

>>> accord = ["non", "oui"]


>>> accord[d]
non
>>> accord[c]
oui

En utilisant les valeurs des variables c et d comme indices pour extraire les lments de la liste accord,
nous confirmons bien que False =0 et True =1.
Le petit script ci-aprs est trs similaire au prcdent. Il nous permet de tester le caractre vrai ou
faux dune chane de caractres :
ch = input("Entrez une chane de caractres quelconque")
if ch:
print("vrai")

27 Ces

variables sont dun type entier un peu particulier : le type boolen . Les variables de ce type ne
peuvent prendre que les deux valeurs True et False (en ralit, 1 et 0).

Vracit/fausset dune expression

55

else:
print("faux")

Vous obtiendrez faux pour toute chane vide, et vrai pour toute chane contenant au moins un
caractre. Vous pourriez de la mme manire tester la vracit dune liste, et constater quune liste
vide est fausse, alors quune liste ayant un contenu quelconque est vraie28.
Linstruction if ch:, la troisime ligne de cet exemple, est donc quivalente une instruction du
type if ch !="":, du moins de notre point de vue dtres humains. Pour lordinateur, cependant, ce
nest pas tout fait pareil. Pour lui, linstruction if ch: consiste vrifier directement si la valeur de
la variable ch est une chane vide ou non, comme nous venons de le voir. Cependant, la seconde
formulation if ch != "": lui impose de commencer par comparer le contenu de ch la valeur dune
autre donne que nous lui fournissons par notre programme (une chane vide), puis valuer ensuite si le
rsultat de cette comparaison est lui-mme vrai ou faux (ou en dautres termes, vrifier si ce
rsultat est lui-mme True ou False). Cela lui demande donc deux oprations successives, l o la
premire formulation ne lui en demande quune seule. La premire formulation est donc plus
performante.
Pour les mmes raisons, dans un script tel celui-ci :
ch =input("Veuillez entrer un nombre : ")
n =int(ch)
if n % 2:
print("Il s'agit d'un nombre impair.")
else:
print("Il s'agit d'un nombre pair.")

il est plus efficace de programmer la troisime ligne comme nous lavons fait ci-dessus, plutt que
dcrire explicitement if n % 2 != 0, car cette formulation imposerait lordinateur deffectuer
deux comparaisons successives au lieu dune seule.
Ce raisonnement proche de la machine vous paratra probablement un peu subtil au dbut, mais
croyez bien que cette forme dcriture vous deviendra trs vite tout fait familire.

Rvision
Dans ce qui suit, nous nallons pas apprendre de nouveaux concepts mais simplement utiliser tout ce
que nous connaissons dj, pour raliser de vrais petits programmes.

Contrle du flux utilisation dune liste simple


Commenons par un petit retour sur les branchements conditionnels (il sagit peut-tre l du groupe
dinstructions le plus important, dans nimporte quel langage !) :
# Utilisation dune liste et de branchements conditionnels
print("Ce script recherche le plus grand de trois nombres")
print("Veuillez entrer trois nombres spars par des virgules : ")
ch =input()
# Note : l'association des fonctions eval() et list() permet de convertir
28 Les

autres structures de donnes se comportent dune manire similaire. Les tuples et les dictionnaires que
vous tudierez au chapitre 10 sont galement considrs comme faux lorsquils sont vides, et vrais lorsquils
possdent un contenu.

56

Fonctions prdfinies

# en liste toute chane de valeurs spares par des virgules :29


nn = list(eval(ch))
max, index = nn[0], 'premier'
if nn[1] > max:
# ne pas omettre le double point !
max = nn[1]
index = 'second'
if nn[2] > max:
max = nn[2]
index = 'troisime'
print("Le plus grand de ces nombres est", max)
print("Ce nombre est le", index, "de votre liste.")

Dans cet exercice, vous retrouvez nouveau le concept de bloc dinstructions , dj abondamment
comment aux chapitres 3 et 4, et que vous devez absolument assimiler. Pour rappel, les blocs dinstructions sont dlimits par lindentation. Aprs la premire instruction if, par exemple, il y a deux
lignes indentes dfinissant un bloc dinstructions. Ces instructions ne seront excutes que si la
condition nn[1] > max est vraie.
La ligne suivante, par contre (celle qui contient la deuxime instruction if ) nest pas indente. Cette
ligne se situe donc au mme niveau que celles qui dfinissent le corps principal du programme. Linstruction contenue dans cette ligne est donc toujours excute, alors que les deux suivantes (qui
constituent encore un autre bloc) ne sont excutes que si la condition nn[2] > max est vraie.
En suivant la mme logique, on voit que les instructions des deux dernires lignes font partie du bloc
principal et sont donc toujours excutes.

Boucle while instructions imbriques


Continuons dans cette voie en imbriquant dautres structures :
1#
2#
3#
4#
5#
6#
7#
8#
9#
10#
11#
12#
13#
14#
15#
16#
17#
18#
19#
20#
21#

# Instructions composes <while> - <if> - <elif> - <else>


print('Choisissez un nombre de 1 3 (ou 0 pour terminer) ', end=' ')
ch = input()
a = int(ch)
# conversion de la chane entre en entier
while a:
# quivalent : < while a != 0: >
if a == 1:
print("Vous avez choisi un :")
print("le premier, l'unique, l'unit ..."
elif a == 2:
print("Vous prfrez le deux :")
print("la paire, le couple, le duo ...")
elif a == 3:
print("Vous optez pour le plus grand des trois :")
print("le trio, la trinit, le triplet ...")
else :
print("Un nombre entre UN et TROIS, s.v.p.")
print(Choisissez un nombre de 1 3 (ou 0 pour terminer) , end =' ')
a = int(input())
print("Vous avez entr zro :")
print("Lexercice est donc termin.")

29 En fait,

la fonction eval() value le contenu de la chane fournie en argument comme tant une expression
Python dont elle doit renvoyer le rsultat. Par exemple : eval("7 + 5") renvoie lentier 12. Si on lui fournit
une chane de valeurs spares par des virgules, cela correspond pour elle un tuple. Les tuples sont des
squences apparentes aux listes. Ils seront abords au chapitre 10 (cf. page 152).

Rvision

57

Nous retrouvons ici une boucle while, associe un groupe dinstructions if, elif et else. Notez bien
cette fois encore comment la structure logique du programme est cre laide des indentations (... et
noubliez pas le caractre : la fin de chaque ligne den-tte !).
la ligne 6, linstruction while est utilise comme expliqu la page 55 : pour la comprendre, il vous
suffit de vous rappeler que toute valeur numrique autre que zro est considre comme vraie par
linterprteur Python. Vous pouvez remplacer cette forme dcriture par while a != 0 : si vous
prfrez (rappelons ce sujet que loprateur de comparaison != signifie est diffrent de ), mais
cest moins efficace.
Cette boucle while relance le questionnement aprs chaque rponse de lutilisateur (du moins
jusqu ce que celui-ci dcide de quitter en entrant une valeur nulle).
Dans le corps de la boucle, nous trouvons le groupe dinstructions if, elif et else (de la ligne 7 la
ligne 17), qui aiguille le flux du programme vers les diffrentes rponses, ensuite une instruction
print() et une instruction input() (lignes 18 et 19) qui seront excutes dans tous les cas de figure :
notez bien leur niveau dindentation, qui est le mme que celui du bloc if, elif et else. Aprs ces
instructions, le programme boucle et lexcution reprend linstruction while (ligne 6).
la ligne 19, nous utilisons la composition pour crire un code plus compact, qui est quivalent aux
lignes 4 et 5 rassembles en une seule.
Les deux dernires instructions print() (lignes 20 et 21) ne sont excutes qu la sortie de la boucle.
Exercices
6.5 Que fait le programme ci-dessous, dans les quatre cas o lon aurait dfini au pralable que la
variable a vaut 1, 2, 3 ou 15 ?
if a !=2:
print('perdu')
elif a ==3:
print('un instant, s.v.p.')
else :
print('gagn')

6.6

Que font ces programmes ?


a)

b)

c)

a = 5
b = 2
if (a==5) & (b<2):
print('"&" signifie "et"; on peut aussi utiliser\
le mot "and"')
a, b = 2, 4
if (a==4) or (b!=4):
print('gagn')
elif (a==4) or (b==4):
print('presque gagn')
a = 1
if not a:
print('gagn')
elif a:
print('perdu')

58

Fonctions prdfinies

6.7

Reprendre le programme c) avec a = 0 au lieu de a = 1.


Que se passe-t-il ? Conclure !

6.8

crire un programme qui, tant donnes deux bornes entires a et b, additionne les nombres
multiples de 3 et de 5 compris entre ces bornes. Prendre par exemple a = 0, b = 32 ; le rsultat
devrait tre alors 0 + 15 + 30 = 45.
Modifier lgrement ce programme pour quil additionne les nombres multiples de 3 ou de 5
compris entre les bornes a et b. Avec les bornes 0 et 32, le rsultat devrait donc tre : 0 + 3 + 5
+ 6 + 9 + 10 + 12 + 15 + 18 + 20 + 21 + 24 + 25 + 27 + 30 = 225.

6.9

Dterminer si une anne (dont le millsime est introduit par lutilisateur) est bissextile ou non.
Une anne A est bissextile si A est divisible par 4. Elle ne lest cependant pas si A est un
multiple de 100, moins que A ne soit multiple de 400.

6.10 Demander lutilisateur son nom et son sexe (M ou F). En fonction de ces donnes, afficher
Cher Monsieur ou Chre Mademoiselle suivi du nom de la personne.
6.11 Demander lutilisateur dentrer trois longueurs a, b, c. laide de ces trois longueurs,
dterminer sil est possible de construire un triangle. Dterminer ensuite si ce triangle est
rectangle, isocle, quilatral ou quelconque. Attention : un triangle rectangle peut tre
isocle.
6.12 Demander lutilisateur quil entre un nombre. Afficher ensuite : soit la racine carre de ce
nombre, soit un message indiquant que la racine carre de ce nombre ne peut tre calcule.
6.13 Convertir une note scolaire N quelconque, entre par lutilisateur sous forme de points (par
exemple 27 sur 85), en une note standardise suivant le code ci-aprs :
Note
N >= 80 %
80 % > N >= 60 %
60 % > N >= 50 %
50 % > N >= 40 %
N < 40 %

Apprciation
A
B
C
D
E

6.14 Soit la liste suivante :


['Jean-Michel', 'Marc', 'Vanessa', 'Anne', 'Maximilien',
'Alexandre-Benot', 'Louise']

crivez un script qui affiche chacun de ces noms avec le nombre de caractres correspondant.
6.15 crire une boucle de programme qui demande lutilisateur dentrer des notes dlves. La
boucle se terminera seulement si lutilisateur entre une valeur ngative. Avec les notes ainsi
entres, construire progressivement une liste. Aprs chaque entre dune nouvelle note (et
donc chaque itration de la boucle), afficher le nombre de notes entres, la note la plus
leve, la note la plus basse, la moyenne de toutes les notes.
6.16 crivez un script qui affiche la valeur de la force de gravitation sexerant entre deux masses
de 10 000 kg , pour des distances qui augmentent suivant une progression gomtrique de
raison 2, partir de 5 cm (0,05 mtre).
11 mm'
La force de gravitation est rgie par la formule F =6,67 .10 2
d

Rvision

59

Exemple daffichage :
d
d
d
d

=
=
=
=

etc.

.05 m
.1 m
.2 m
.4 m

:
:
:
:

la
la
la
la

force
force
force
force

vaut
vaut
vaut
vaut

2.668 N
0.667 N
0.167 N
0.0417 N

7
Fonctions originales

La programmation est lart dapprendre un ordinateur comment accomplir des tches quil
ntait pas capable de raliser auparavant. Lune des mthodes les plus intressantes pour y arriver
consiste ajouter de nouvelles instructions au langage de programmation que vous utilisez, sous la
forme de fonctions originales.

Dfinir une fonction


Les scripts que vous avez crits jusqu prsent taient chaque fois trs courts, car leur objectif tait
seulement de vous faire assimiler les premiers lments du langage. Lorsque vous commencerez
dvelopper de vritables projets, vous serez confronts des problmes souvent fort complexes, et les
lignes de programme vont commencer saccumuler...
Lapproche efficace dun problme complexe consiste souvent le dcomposer en plusieurs
sous-problmes plus simples qui seront tudis sparment (ces sous-problmes peuvent
ventuellement tre eux-mmes dcomposs leur tour, et ainsi de suite). Or il est important que
cette dcomposition soit reprsente fidlement dans les algorithmes30 pour que ceux-ci restent clairs.
Dautre part, il arrivera souvent quune mme squence dinstructions doive tre utilise plusieurs
reprises dans un programme, et on souhaitera bien videmment ne pas avoir la reproduire
systmatiquement.
Les fonctions31 et les classes dobjets sont diffrentes structures de sous-programmes qui ont t
imagines par les concepteurs des langages de haut niveau afin de rsoudre les difficults voques
ci-dessus. Nous allons commencer par dcrire ici la dfinition de fonctions sous Python. Les objets et les
classes seront examins plus loin.
Nous avons dj rencontr diverses fonctions pr-programmes. Voyons prsent comment en
dfinir nous-mmes de nouvelles.
La syntaxe Python pour la dfinition dune fonction est la suivante :
30 On

appelle algorithme la squence dtaille de toutes les oprations effectuer pour rsoudre un
problme.
31 Il existe aussi dans dautres langages des routines (parfois appels sous-programmes) et des procdures.
Il nexiste pas de routines en Python. Quant au terme de fonction, il dsigne la fois les fonctions au sens
strict (qui fournissent une valeur en retour), et les procdures (qui n en fournissent pas).

62

Fonctions originales

def nomDeLaFonction(liste de paramtres):


...
bloc d'instructions
...

Vous pouvez choisir nimporte quel nom pour la fonction que vous crez, lexception des mots
rservs du langage32, et la condition de nutiliser aucun caractre spcial ou accentu (le
caractre soulign _ est permis). Comme cest le cas pour les noms de variables, il vous est
conseill dutiliser surtout des lettres minuscules, notamment au dbut du nom (les noms
commenant par une majuscule seront rservs aux classes que nous tudierons plus loin).
Comme les instructions if et while que vous connaissez dj, linstruction def est une instruction
compose. La ligne contenant cette instruction se termine obligatoirement par un double point,
lequel introduit un bloc dinstructions que vous ne devez pas oublier dindenter.
La liste de paramtres spcifie quelles informations il faudra fournir en guise darguments lorsque
lon voudra utiliser cette fonction (les parenthses peuvent parfaitement rester vides si la
fonction ne ncessite pas darguments).
Une fonction sutilise pratiquement comme une instruction quelconque. Dans le corps dun
programme, un appel de fonction est constitu du nom de la fonction suivi de parenthses.
Si cest ncessaire, on place dans ces parenthses le ou les arguments que lon souhaite
transmettre la fonction. Il faudra en principe fournir un argument pour chacun des paramtres
spcifis dans la dfinition de la fonction, encore quil soit possible de dfinir pour ces paramtres
des valeurs par dfaut (voir plus loin).

Fonction simple sans paramtres


Pour notre premire approche concrte des fonctions, nous allons travailler nouveau en mode
interactif. Le mode interactif de Python est en effet idal pour effectuer des petits tests comme ceux
qui suivent. Cest une facilit que noffrent pas tous les langages de programmation !
>>> def table7():
...
n = 1
...
while n <11 :
...
print(n * 7, end =' ')
...
n = n +1
...

En entrant ces quelques lignes, nous avons dfini une fonction trs simple qui calcule et affiche les 10
premiers termes de la table de multiplication par 7. Notez bien les parenthses 33, le double point, et
lindentation du bloc dinstructions qui suit la ligne den-tte (cest ce bloc dinstructions qui constitue
le corps de la fonction proprement dite).
Pour utiliser la fonction que nous venons de dfinir, il suffit de lappeler par son nom. Ainsi :
>>> table7()

provoque laffichage de :
32 La liste complte des mots rservs Python se trouve page 14.
33 Un nom de fonction doit toujours tre accompagn de parenthses,

mme si la fonction n utilise aucun


paramtre. Il en rsulte une convention d criture qui stipule que dans un texte quelconque traitant de
programmation dordinateur, un nom de fonction soit toujours accompagn d une paire de parenthses
vides. Nous respecterons cette convention dans la suite de ce texte.

Dfinir une fonction

63

7 14 21 28 35 42 49 56 63 70

Nous pouvons maintenant rutiliser cette fonction plusieurs reprises, autant de fois que nous le
souhaitons. Nous pouvons galement lincorporer dans la dfinition dune autre fonction, comme dans
lexemple ci-dessous :
>>> def table7triple():
...
print('La table par 7 en triple exemplaire :')
...
table7()
...
table7()
...
table7()
...

Utilisons cette nouvelle fonction, en entrant la commande :


>>> table7triple()

laffichage rsultant devrait tre :


La table par 7 en triple exemplaire :
7 14 21 28 35 42 49 56 63 70
7 14 21 28 35 42 49 56 63 70
7 14 21 28 35 42 49 56 63 70

Une premire fonction peut donc appeler une deuxime fonction, qui elle-mme en appelle une
troisime, etc. Au stade o nous sommes, vous ne voyez peut-tre pas encore trs bien lutilit de tout
cela, mais vous pouvez dj noter deux proprits intressantes :

Crer une nouvelle fonction vous offre lopportunit de donner un nom tout un ensemble dinstructions. De cette manire, vous pouvez simplifier le corps principal dun programme, en
dissimulant un algorithme secondaire complexe sous une commande unique, laquelle vous
pouvez donner un nom trs explicite, en franais si vous voulez.
Crer une nouvelle fonction peut servir raccourcir un programme, par limination des portions
de code qui se rptent. Par exemple, si vous devez afficher la table par 7 plusieurs fois dans un
mme programme, vous navez pas rcrire chaque fois lalgorithme qui accomplit ce travail.
Une fonction est donc en quelque sorte une nouvelle instruction personnalise, que vous ajoutez
vous-mme librement votre langage de programmation.

Fonction avec paramtre


Dans nos derniers exemples, nous avons dfini et utilis une fonction qui affiche les termes de la table
de multiplication par 7. Supposons prsent que nous voulions faire de mme avec la table par 9. Nous
pouvons bien entendu rcrire entirement une nouvelle fonction pour cela. Mais si nous nous
intressons plus tard la table par 13, il nous faudra encore recommencer. Ne serait-il donc pas plus
intressant de dfinir une fonction qui soit capable dafficher nimporte quelle table, la demande ?
Lorsque nous appellerons cette fonction, nous devrons bien videmment pouvoir lui indiquer quelle
table nous souhaitons afficher. Cette information que nous voulons transmettre la fonction au
moment mme o nous lappelons sappelle un argument. Nous avons dj rencontr plusieurs
reprises des fonctions intgres qui utilisent des arguments. La fonction sin(a), par exemple, calcule le
sinus de langle a. La fonction sin() utilise donc la valeur numrique de a comme argument pour
effectuer son travail.

64

Fonctions originales

Dans la dfinition dune telle fonction, il faut prvoir une variable particulire pour recevoir largument transmis. Cette variable particulire sappelle un paramtre. On lui choisit un nom en respectant
les mmes rgles de syntaxe que dhabitude (pas de lettres accentues, etc.), et on place ce nom entre
les parenthses qui accompagnent la dfinition de la fonction.
Voici ce que cela donne dans le cas qui nous intresse :
>>> def table(base):
...
n = 1
...
while n <11 :
...
print(n * base, end =' ')
...
n = n +1

La fonction table() telle que dfinie ci-dessus utilise le paramtre base pour calculer les dix premiers
termes de la table de multiplication correspondante.
Pour tester cette nouvelle fonction, il nous suffit de lappeler avec un argument. Exemples :
>>> table(13)
13 26 39 52 65 78 91 104 117 130
>>> table(9)
9 18 27 36 45 54 63 72 81 90

Dans ces exemples, la valeur que nous indiquons entre parenthses lors de lappel de la fonction (et
qui est donc un argument) est automatiquement affecte au paramtre base. Dans le corps de la
fonction, base joue le mme rle que nimporte quelle autre variable. Lorsque nous entrons la
commande table(9), nous signifions la machine que nous voulons excuter la fonction table() en
affectant la valeur 9 la variable base.

Utilisation dune variable comme argument


Dans les 2 exemples qui prcdent, largument que nous avons utilis en appelant la fonction table()
tait chaque fois une constante (la valeur 13, puis la valeur 9). Cela nest nullement obligatoire. Largument que nous utilisons dans lappel dune fonction peut tre une variable lui aussi, comme dans
lexemple ci-dessous. Analysez bien cet exemple, essayez-le concrtement, et dcrivez le mieux
possible dans votre cahier dexercices ce que vous obtenez, en expliquant avec vos propres mots ce qui
se passe. Cet exemple devrait vous donner un premier aperu de lutilit des fonctions pour accomplir
simplement des tches complexes :
>>> a = 1
>>> while a <20:
...
table(a)
...
a = a +1
...

Remarque importante
Dans lexemple ci-dessus, largument que nous passons la fonction table() est le contenu de la
variable a. lintrieur de la fonction, cet argument est affect au paramtre base, qui est une tout
autre variable. Notez donc bien ds prsent que :

Dfinir une fonction

65

Le nom dune variable que nous passons comme argument na rien voir avec le nom du
paramtre correspondant dans la fonction.

Ces noms peuvent tre identiques si vous le voulez, mais vous devez bien comprendre quils ne
dsignent pas la mme chose (en dpit du fait quils puissent ventuellement contenir une valeur
identique).
Exercice
7.1 Importez le module turtle pour pouvoir effectuer des dessins simples.

Vous allez dessiner une srie de triangles quilatraux de diffrentes couleurs.


Pour ce faire, dfinissez dabord une fonction triangle() capable de dessiner un triangle dune
couleur bien dtermine (ce qui signifie donc que la dfinition de votre fonction doit
comporter un paramtre pour recevoir le nom de cette couleur).
Utilisez ensuite cette fonction pour reproduire ce mme triangle en diffrents endroits, en
changeant de couleur chaque fois.

Fonction avec plusieurs paramtres


La fonction table() est certainement intressante, mais elle naffiche toujours que les dix premiers
termes de la table de multiplication, alors que nous pourrions souhaiter quelle en affiche dautres.
Qu cela ne tienne. Nous allons lamliorer en lui ajoutant des paramtres supplmentaires, dans une
nouvelle version que nous appellerons cette fois tableMulti() :
>>> def tableMulti(base, debut, fin):
...
print('Fragment de la table de multiplication par', base, ':')
...
n = debut
...
while n <= fin :
...
print(n, 'x', base, '=', n * base)
...
n = n +1

Cette nouvelle fonction utilise donc trois paramtres : la base de la table comme dans lexemple
prcdent, lindice du premier terme afficher, lindice du dernier terme afficher.
Essayons cette fonction en entrant par exemple :
>>> tableMulti(8, 13, 17)

ce qui devrait provoquer laffichage de :


Fragment
13 x 8 =
14 x 8 =
15 x 8 =
16 x 8 =
17 x 8 =

de la table de multiplication par 8 :


104
112
120
128
136

Notes

Pour dfinir une fonction avec plusieurs paramtres, il suffit dinclure ceux-ci entre les
parenthses qui suivent le nom de la fonction, en les sparant laide de virgules.

66

Fonctions originales

Lors de lappel de la fonction, les arguments utiliss doivent tre fournis dans le mme ordre que
celui des paramtres correspondants (en les sparant eux aussi laide de virgules). Le premier
argument sera affect au premier paramtre, le second argument sera affect au second
paramtre, et ainsi de suite.
titre dexercice, essayez la squence dinstructions suivantes et dcrivez dans votre cahier
dexercices le rsultat obtenu :
>>> t, d, f = 11, 5, 10
>>> while t<21:
...
tableMulti(t,d,f)
...
t, d, f = t +1, d +3, f +5
...

Variables locales, variables globales


Lorsque nous dfinissons des variables lintrieur du corps dune fonction, ces variables ne sont
accessibles qu la fonction elle-mme. On dit que ces variables sont des variables locales la fonction.
Cest par exemple le cas des variables base, debut, fin et n dans lexercice prcdent.
Chaque fois que la fonction tableMulti() est appele, Python rserve pour elle (dans la mmoire de
lordinateur) un nouvel espace de noms34. Les contenus des variables base, debut, fin et n sont stocks
dans cet espace de noms qui est inaccessible depuis lextrieur de la fonction. Ainsi par exemple, si nous
essayons dafficher le contenu de la variable base juste aprs avoir effectu lexercice ci-dessus, nous
obtenons un message derreur :
>>> print(base)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'base' is not defined

La machine nous signale clairement que le symbole base lui est inconnu, alors quil tait correctement
imprim par la fonction tableMulti() elle-mme. Lespace de noms qui contient le symbole base est
strictement rserv au fonctionnement interne de tableMulti(), et il est automatiquement dtruit ds
que la fonction a termin son travail.
Les variables dfinies lextrieur dune fonction sont des variables globales. Leur contenu est visible de lintrieur dune fonction, mais la fonction ne peut pas le modifier. Exemple :
>>> def mask():
...
p = 20
...
print(p, q)
...
>>> p, q = 15, 38
>>> mask()
20 38
>>> print(p, q)
15 38

Analysons attentivement cet exemple :

34 Ce concept despace de noms

sera approfondi progressivement. Vous apprendrez galement plus loin que les
fonctions sont en fait des objets dont on cre chaque fois une nouvelle instance lorsquon les appelle.

Variables locales, variables globales

67

Nous commenons par dfinir une fonction trs simple (qui nutilise dailleurs aucun paramtre).
lintrieur de cette fonction, une variable p est dfinie, avec 20 comme valeur initiale. Cette variable p
qui est dfinie lintrieur dune fonction sera donc une variable locale.
Une fois la dfinition de la fonction termine, nous revenons au niveau principal pour y dfinir les
deux variables p et q auxquelles nous attribuons les contenus 15 et 38. Ces deux variables dfinies au
niveau principal seront donc des variables globales.
Ainsi le mme nom de variable p a t utilis ici deux reprises, pour dfinir deux variables diffrentes :
lune est globale et lautre est locale. On peut constater dans la suite de lexercice que ces deux
variables sont bel et bien des variables distinctes, indpendantes, obissant une rgle de priorit qui
veut qu lintrieur dune fonction (o elles pourraient entrer en comptition), ce sont les variables
dfinies localement qui ont la priorit.
On constate en effet que lorsque la fonction mask() est lance, la variable globale q y est accessible,
puisquelle est imprime correctement. Pour p, par contre, cest la valeur attribue localement qui est
affiche.
On pourrait croire dabord que la fonction mask() a simplement modifi le contenu de la variable
globale p (puisquelle est accessible). Les lignes suivantes dmontrent quil nen est rien : en dehors de
la fonction mask(), la variable globale p conserve sa valeur initiale.
Tout ceci peut vous paratre compliqu au premier abord. Vous comprendrez cependant trs vite
combien il est utile que des variables soient ainsi dfinies comme tant locales, cest--dire en quelque
sorte confines lintrieur dune fonction. Cela signifie en effet que vous pourrez toujours utiliser
une infinit de fonctions sans vous proccuper le moins du monde des noms de variables qui y sont
utilises : ces variables ne pourront en effet jamais interfrer avec celles que vous aurez vous-mme
dfinies par ailleurs.
Cet tat de choses peut toutefois tre modifi si vous le souhaitez. Il peut se faire par exemple que vous
ayez dfinir une fonction qui soit capable de modifier une variable globale. Pour atteindre ce
rsultat, il vous suffira dutiliser linstruction global. Cette instruction permet dindiquer lintrieur de la dfinition dune fonction quelles sont les variables traiter globalement.
Dans lexemple ci-dessous, la variable a utilise lintrieur de la fonction monter() est non seulement
accessible, mais galement modifiable, parce quelle est signale explicitement comme tant une
variable quil faut traiter globalement. Par comparaison, essayez le mme exercice en supprimant
linstruction global : la variable a nest plus incrmente chaque appel de la fonction.
>>>
...
...
...
...
>>>
>>>
16
>>>
17
>>>

def monter():
global a
a = a+1
print(a)
a = 15
monter()
monter()

68

Fonctions originales

Vraies fonctions et procdures


Pour les puristes, les fonctions que nous avons dcrites jusqu prsent ne sont pas tout fait des
fonctions au sens strict, mais plus exactement des procdures35. Une vraie fonction (au sens strict)
doit en effet renvoyer une valeur lorsquelle se termine. Une vraie fonction peut sutiliser la droite
du signe gale dans des expressions telles que y = sin(a). On comprend aisment que dans cette
expression, la fonction sin() renvoie une valeur (le sinus de largument a) qui est directement affecte
la variable y.
Commenons par un exemple extrmement simple :
>>> def cube(w):
...
return w*w*w
...

Linstruction return dfinit ce que doit tre la valeur renvoye par la fonction. En loccurrence, il
sagit du cube de largument qui a t transmis lors de lappel de la fonction. Exemple :
>>> b = cube(9)
>>> print(b)
729

titre dexemple un peu plus labor, nous allons maintenant modifier quelque peu la fonction
table() sur laquelle nous avons dj pas mal travaill, afin quelle renvoie elle aussi une valeur. Cette
valeur sera en loccurrence une liste (la liste des dix premiers termes de la table de multiplication
choisie). Voil donc une occasion de reparler des listes. Dans la foule, nous en profiterons pour
apprendre encore un nouveau concept :
>>> def table(base):
...
resultat = []
...
n = 1
...
while n < 11:
...
b = n * base
...
resultat.append(b)
...
n = n +1
...
return resultat
...

# resultat est dabord une liste vide

# ajout dun terme la liste


# (voir explications ci-dessous)

Pour tester cette fonction, nous pouvons entrer par exemple :


>>> ta9 = table(9)

Ainsi nous affectons la variable ta9 les dix premiers termes de la table de multiplication par 9, sous la
forme dune liste :
>>> print(ta9)
[9, 18, 27, 36, 45, 54, 63, 72, 81, 90]
>>> print(ta9[0])
9
>>> print(ta9[3])
36
>>> print(ta9[2:5])
[27, 36, 45]
>>>

35 Dans

certains langages de programmation, les fonctions et les procdures sont dfinies l aide
dinstructions diffrentes. Python utilise la mme instruction def pour dfinir les unes et les autres.

Vraies fonctions et procdures

69

(Rappel : le premier lment dune liste correspond lindice 0).

Notes
Comme nous lavons vu dans lexemple prcdent, linstruction return dfinit ce que doit tre la
valeur renvoye par la fonction. En loccurrence, il sagit
ici du contenu de la variable resultat,
cest--dire la liste des nombres gnrs par la fonction36.
Linstruction resultat.append(b) est notre second exemple de lutilisation dun concept
important sur lequel nous reviendrons encore abondamment par la suite : dans cette instruction,
nous appliquons la mthode append() lobjet resultat.
Nous prciserons petit petit ce quil faut entendre par objet en programmation. Pour linstant, admettons simplement que ce terme trs gnral sapplique notamment aux listes de Python. Une
mthode nest en fait rien dautre quune fonction (que vous pouvez dailleurs reconnatre comme
telle la prsence des parenthses), mais une fonction qui est associe un objet. Elle fait partie de la
dfinition de cet objet, ou plus prcisment de la classe particulire laquelle cet objet appartient
(nous tudierons ce concept de classe plus tard).
Mettre en uvre une mthode associe un objet consiste en quelque sorte faire fonctionner cet
objet dune manire particulire. Par exemple, on met en uvre la mthode methode4() dun
objet objet3, laide dune instruction du type : objet3.methode4() , cest--dire le nom de lobjet,
puis le nom de la mthode, relis lun lautre par un point. Ce point joue un rle essentiel : on
peut le considrer comme un vritable oprateur.
Dans notre exemple, nous appliquons donc la mthode append() lobjet resultat, qui est une
liste. Sous Python, les listes constituent donc une classe particulire dobjets, auxquels on peut
effectivement appliquer toute une srie de mthodes. En loccurrence, la mthode append() des
objets listes sert leur ajouter un lment par la fin. Llment ajouter est transmis entre les
parenthses, comme tout argument qui se respecte.
Nous aurions obtenu un rsultat similaire si nous avions utilis la place de cette instruction une
expression telle que resultat = resultat + [b] (loprateur de concatnation fonctionne en
effet aussi avec les listes). Cette faon de procder est cependant moins rationnelle et beaucoup
moins efficace, car elle consiste redfinir chaque itration de la boucle une nouvelle liste resultat, dans laquelle la totalit de la liste prcdente est chaque fois recopie avec ajout dun
lment supplmentaire.
Lorsque lon utilise la mthode append(), par contre, lordinateur procde bel et bien une
modification de la liste existante (sans la recopier dans une nouvelle variable). Cette technique est
donc prfrable, car elle mobilise moins lourdement les ressources de lordinateur, et elle est plus
rapide (surtout lorsquil sagit de traiter des listes volumineuses).
Il nest pas du tout indispensable que la valeur renvoye par une fonction soit affecte une
variable (comme nous lavons fait jusquici dans nos exemples par souci de clart). Ainsi, nous
aurions pu tester les fonction cube() et table() en entrant les commandes :
-

>>> print(cube(9))
>>> print(table(9))
>>> print(table(9)[3])
36 return

peut galement tre utilis sans aucun argument, l intrieur dune fonction, pour provoquer sa
fermeture immdiate. La valeur retourne dans ce cas est l objet None (objet particulier, correspondant
rien ).

70

Fonctions originales
ou encore plus simplement encore :
>>> cube(9)...

Utilisation des fonctions dans un script


Pour cette premire approche des fonctions, nous navons utilis jusquici que le mode interactif de
linterprteur Python.
Il est bien vident que les fonctions peuvent aussi sutiliser dans des scripts. Veuillez donc essayer
vous-mme le petit programme ci-dessous, lequel calcule le volume dune sphre laide de la formule
4
que vous connaissez certainement : V = R 3
3
def cube(n):
return n**3
def volumeSphere(r):
return 4 * 3.1416 * cube(r) / 3
r = input('Entrez la valeur du rayon : ')
print('Le volume de cette sphre vaut', volumeSphere(float(r)))

Notes
bien y regarder, ce programme comporte trois parties : les deux fonctions cube() et
volumeSphere(), et ensuite le corps principal du programme.
Dans le corps principal du programme, on appelle la fonction volumeSphere(), en lui transmettant la
valeur entre par lutilisateur pour le rayon, pralablement convertie en un nombre rel laide de la
fonction intgre float().
lintrieur de la fonction volumeSphere(), il y a un appel de la fonction cube().
Notez bien que les trois parties du programme ont t disposes dans un certain ordre : dabord la
dfinition des fonctions, et ensuite le corps principal du programme. Cette disposition est ncessaire, parce
que linterprteur excute les lignes dinstructions du programme lune aprs lautre, dans lordre o
elles apparaissent dans le code source.
Dans un script, la dfinition des fonctions doit prcder leur utilisation.

Pour vous en convaincre, intervertissez cet ordre (en plaant par exemple le corps principal du
programme au dbut), et prenez note du type de message derreur qui est affich lorsque vous essayez
dexcuter le script ainsi modifi.
En fait, le corps principal dun programme Python constitue lui-mme une entit un peu particulire,
qui est toujours reconnue dans le fonctionnement interne de linterprteur sous le nom rserv
__main__ (le mot main signifie principal , en anglais. Il est encadr par des caractres soulign en double, pour viter toute confusion avec dautres symboles). Lexcution dun script
commence toujours avec la premire instruction de cette entit __main__, o quelle puisse se trouver
dans le listing. Les instructions qui suivent sont alors excutes lune aprs lautre, dans lordre, jus-

Utilisation des fonctions dans un script

71

quau premier appel de fonction. Un appel de fonction est comme un dtour dans le flux de lexcution : au lieu de passer linstruction suivante, linterprteur excute la fonction appele, puis revient
au programme appelant pour continuer le travail interrompu. Pour que ce mcanisme puisse
fonctionner, il faut que linterprteur ait pu lire la dfinition de la fonction avant lentit __main__, et
celle-ci sera donc place en gnral la fin du script.
Dans notre exemple, lentit __main__ appelle une premire fonction qui elle-mme en appelle une
deuxime. Cette situation est trs frquente en programmation. Si vous voulez comprendre
correctement ce qui se passe dans un programme, vous devez donc apprendre lire un script, non pas
de la premire la dernire ligne, mais plutt en suivant un cheminement analogue ce qui se passe
lors de lexcution de ce script. Cela signifie donc concrtement que vous devrez souvent analyser un
script en commenant par ses dernires lignes !

Modules de fonctions
Afin que vous puissiez mieux comprendre encore la distinction entre la dfinition dune fonction et
son utilisation au sein dun programme, nous vous suggrons de placer frquemment vos dfinitions
de fonctions dans un module Python, et le programme qui les utilise dans un autre.
Exemple :
On souhaite raliser la srie de dessins
ci-contre, laide du module turtle :
crivez les lignes de code suivantes, et
sauvegardez-les dans un fichier auquel vous
donnerez le nom dessins_tortue.py :
from turtle import *
def carre(taille, couleur):
"fonction qui dessine un carr de taille et de couleur dtermines"
color(couleur)
c =0
while c <4:
forward(taille)
right(90)
c = c +1

Vous pouvez remarquer que la dfinition de la fonction carre() commence par une chane de
caractres. Cette chane ne joue aucun rle fonctionnel dans le script : elle est traite par Python
comme un simple commentaire, mais qui est mmoris part dans un systme de documentation interne
automatique, lequel pourra ensuite tre exploit par certains utilitaires et diteurs intelligents .
Si vous programmez dans lenvironnement IDLE, par exemple, vous verrez apparatre cette chane
documentaire dans une bulle daide , chaque fois que vous ferez appel aux fonctions ainsi
documentes.
En fait, Python place cette chane dans une variable spciale dont le nom est __doc__ (le mot doc
entour de deux paires de caractres soulign ), et qui est associe lobjet fonction comme tant
lun de ses attributs (vous en apprendrez davantage au sujet de ces attributs lorsque nous aborderons

72

Fonctions originales

les classes dobjets, page 166). Ainsi, vous pouvez vous-mme retrouver la chane de documentation
dune fonction quelconque en affichant le contenu de cette variable. Exemple :
>>> def essai():
...
"Cette fonction est bien documente mais ne fait presque rien."
...
print("rien signaler")
...
>>> essai()
rien signaler
>>> print(essai.__doc__)
Cette fonction est bien documente mais ne fait presque rien.

Prenez donc la peine dincorporer une telle chane explicative dans toutes vos dfinitions de fonctions
futures : il sagit l dune pratique hautement recommandable.
Le fichier que vous aurez cr ainsi est dornavant un vritable module de fonctions Python, au mme
titre que les modules turtle ou math que vous connaissez dj. Vous pouvez donc lutiliser dans nimporte quel autre script, comme celui-ci, par exemple, qui effectuera le travail demand :
from dessins_tortue import *
up()
goto(-150, 50)

# relever le crayon
# reculer en haut gauche

# dessiner dix carrs rouges, aligns :


i = 0
while i < 10:
down()
# abaisser le crayon
carre(25, 'red')
# tracer un carr
up()
# relever le crayon
forward(30)
# avancer + loin
i = i +1
a = input()
# attendre

Attention
Vous pouvez priori nommer vos modules de fonctions comme bon vous semble.
Sachez cependant quil vous sera impossible dimporter un module si son nom est lun
des 33 mots rservs Python signals la page 14, car le nom du module import
deviendrait une variable dans votre script, et les mots rservs ne peuvent pas tre
utiliss comme noms de variables. Rappelons aussi quil vous faut viter de donner vos
modules et tous vos scripts en gnral le mme nom que celui dun module Python
prexistant, sinon vous devez vous attendre des conflits. Par exemple, si vous donnez
le nom turtle.py un exercice dans lequel vous avez plac une instruction dimportation
du module turtle, cest lexercice lui-mme que vous allez importer !

Modules de fonctions

73

74

Fonctions originales

Exercices
7.2 Dfinissez une fonction ligneCar(n, ca) qui renvoie une chane de n caractres ca.

7.3

Dfinissez une fonction surfCercle(R). Cette fonction doit renvoyer la surface (laire) dun
cercle dont on lui a fourni le rayon R en argument. Par exemple, lexcution de linstruction :
print(surfCercle(2.5)) doit donner le rsultat : 19.63495...

7.4

Dfinissez une fonction volBoite(x1,x2,x3) qui renvoie le volume dune bote


paralllpipdique dont on fournit les trois dimensions x1, x2, x3 en arguments.
Par exemple, lexcution de linstruction :
print(volBoite(5.2, 7.7, 3.3)) doit donner le rsultat : 132.132.

7.5

Dfinissez une fonction maximum(n1,n2,n3) qui renvoie le plus grand de 3 nombres n1, n2,
n3 fournis en arguments. Par exemple, lexcution de linstruction :
print(maximum(2,5,4)) doit donner le rsultat : 5.

7.6

Compltez le module de fonctions graphiques dessins_tortue.py dcrit la page 71.


Commencez par ajouter un paramtre angle la fonction carre(), de manire ce que les
carrs puissent tre tracs dans diffrentes orientations.
Dfinissez ensuite une fonction triangle(taille, couleur, angle) capable de dessiner un
triangle quilatral dune taille, dune couleur et dune orientation bien dtermines.
Testez votre module laide dun programme qui fera appel ces fonctions plusieurs
reprises, avec des arguments varis pour dessiner une srie de carrs et de triangles :

7.7

Ajoutez au module de lexercice prcdent une fonction etoile5() spcialise dans le dessin
dtoiles 5 branches. Dans votre programme principal, insrez une boucle qui dessine une
range horizontale de de 9 petites toiles de tailles varies :

Modules de fonctions

75

7.8

Ajoutez au module de lexercice prcdent une fonction etoile6() capable de dessiner une
toile 6 branches, elle-mme constitue de deux triangles quilatraux imbriqus. Cette
nouvelle fonction devra faire appel la fonction triangle() dfinie prcdemment.
Votre programme principal dessinera galement une srie de ces toiles :

7.9

Dfinissez une fonction compteCar(ca,ch) qui renvoie le nombre de fois que lon rencontre le
caractre ca dans la chane de caractres ch. Par exemple, lexcution de linstruction :
print(compteCar(e, Cette phrase est un exemple)) doit donner le rsultat : 7

7.10 Dfinissez une fonction indexMax(liste) qui renvoie lindex de llment ayant la valeur la
plus leve dans la liste transmise en argument. Exemple dutilisation :
serie = [5, 8, 2, 1, 9, 3, 6, 7]
print(indexMax(serie))
4

7.11 Dfinissez une fonction nomMois(n) qui renvoie le nom du nime mois de lanne.
Par exemple, lexcution de linstruction :
print(nomMois(4)) doit donner le rsultat : Avril.
7.12 Dfinissez une fonction inverse(ch) qui permette dinverser les lordre des caractres dune
chane quelconque. La chane inverse sera renvoye au programme appelant.
7.13 Dfinissez une fonction compteMots(ph) qui renvoie le nombre de mots contenus dans la
phrase ph. On considre comme mots les ensembles de caractres inclus entre des espaces.

76

Fonctions originales

Typage des paramtres


Vous avez appris que le typage des variables sous Python est un typage dynamique, ce qui signifie que le
type dune variable est dfini au moment o on lui affecte une valeur. Ce mcanisme fonctionne aussi
pour les paramtres dune fonction. Le type dun paramtre devient automatiquement le mme que
celui de largument qui a t transmis la fonction. Exemple :
>>> def afficher3fois(arg):
...
print(arg, arg, arg)
...
>>> afficher3fois(5)
5 5 5
>>> afficher3fois('zut')
zut zut zut
>>> afficher3fois([5, 7])
[5, 7] [5, 7] [5, 7]
>>> afficher3fois(6**2)
36 36 36

Dans cet exemple, vous pouvez constater que la mme fonction afficher3fois() accepte dans tous les
cas largument quon lui transmet, que cet argument soit un nombre, une chane de caractres, une
liste, ou mme une expression. Dans ce dernier cas, Python commence par valuer lexpression, et cest le
rsultat de cette valuation qui est transmis comme argument la fonction.

Valeurs par dfaut pour les paramtres


Dans la dfinition dune fonction, il est possible (et souvent souhaitable) de dfinir un argument par
dfaut pour chacun des paramtres. On obtient ainsi une fonction qui peut tre appele avec une partie
seulement des arguments attendus. Exemples :
>>> def politesse(nom, vedette ='Monsieur'):
...
print("Veuillez agrer ,", vedette, nom, ", mes salutations cordiales.")
...
>>> politesse('Dupont')
Veuillez agrer , Monsieur Dupont , mes salutations cordiales.
>>> politesse('Durand', 'Mademoiselle')
Veuillez agrer , Mademoiselle Durand , mes salutations cordiales.

Lorsque lon appelle cette fonction en ne lui fournissant que le premier argument, le second reoit
tout de mme une valeur par dfaut. Si lon fournit les deux arguments, la valeur par dfaut pour le
deuxime est tout simplement ignore.
Vous pouvez dfinir une valeur par dfaut pour tous les paramtres, ou une partie dentre eux
seulement. Dans ce cas, cependant, les paramtres sans valeur par dfaut doivent prcder les autres dans la
liste. Par exemple, la dfinition ci-dessous est incorrecte :
>>> def politesse(vedette ='Monsieur', nom):

Valeurs par dfaut pour les paramtres

77

Autre exemple :
>>> def question(annonce, essais =4, please ='Oui ou non, s.v.p.!'):
...
while essais >0:
...
reponse = input(annonce)
...
if reponse in ('o', 'oui','O','Oui','OUI'):
...
return 1
...
if reponse in ('n','non','N','Non','NON'):
...
return 0
...
print(please)
...
essais = essais-1
...
>>>

Cette fonction peut tre appele de diffrentes faons, telles par exemple :
rep = question('Voulez-vous vraiment terminer ? ')

ou bien :
rep = question('Faut-il effacer ce fichier ? ', 3)

ou mme encore :
rep = question('Avez-vous compris ? ', 2, 'Rpondez par oui ou par non !')

Prenez la peine dessayer et de dcortiquer cet exemple.

Arguments avec tiquettes


Dans la plupart des langages de programmation, les arguments que lon fournit lors de lappel dune
fonction doivent tre fournis exactement dans le mme ordre que celui des paramtres qui leur
correspondent dans la dfinition de la fonction.
Python autorise cependant une souplesse beaucoup plus grande. Si les paramtres annoncs dans la
dfinition de la fonction ont reu chacun une valeur par dfaut, sous la forme dj dcrite ci-dessus,
on peut faire appel la fonction en fournissant les arguments correspondants dans nimporte quel ordre,
la condition de dsigner nommment les paramtres correspondants. Exemple :
>>> def oiseau(voltage=100, etat='allum', action='danser la java'):
...
print('Ce perroquet ne pourra pas', action)
...
print('si vous le branchez sur', voltage, 'volts !')
...
print("L'auteur de ceci est compltement", etat)
...
>>> oiseau(etat='givr', voltage=250, action='vous approuver')
Ce perroquet ne pourra pas vous approuver
si vous le branchez sur 250 volts !
L'auteur de ceci est compltement givr
>>> oiseau()
Ce perroquet ne pourra pas danser la java
si vous le branchez sur 100 volts !
L'auteur de ceci est compltement allum

78

Fonctions originales

Exercices
7.14 Modifiez la fonction volBoite(x1,x2,x3) que vous avez dfinie dans un exercice prcdent, de
manire ce quelle puisse tre appele avec trois, deux, un seul, ou mme aucun argument.
Utilisez pour ceux ci des valeurs par dfaut gales 10.
Par exemple :
print(volBoite())
doit donner le rsultat : 1000
print(volBoite(5.2))
doit donner le rsultat : 520.0
print(volBoite(5.2, 3))
doit donner le rsultat : 156.0

7.15 Modifiez la fonction volBoite(x1,x2,x3) ci-dessus de manire ce quelle puisse tre appele
avec un, deux, ou trois arguments. Si un seul est utilis, la bote est considre comme cubique
(largument tant larte de ce cube). Si deux sont utiliss, la bote est considre comme un
prisme base carre (auquel cas le premier argument est le ct du carr, et le second la
hauteur du prisme). Si trois arguments sont utiliss, la bote est considre comme un
paralllpipde. Par exemple :
print(volBoite())
doit donner le rsultat : -1 (indication dune erreur)
print(volBoite(5.2))
doit donner le rsultat : 140.608
print(volBoite(5.2, 3))
doit donner le rsultat : 81.12
print(volBoite(5.2, 3, 7.4))
doit donner le rsultat : 115.44
7.16 Dfinissez une fonction changeCar(ch,ca1,ca2,debut,fin) qui remplace tous les caractres
ca1 par des caractres ca2 dans la chane de caractres ch, partir de lindice debut et jusqu
lindice fin, ces deux derniers arguments pouvant tre omis (et dans ce cas la chane est traite
dune extrmit lautre). Exemples de la fonctionnalit attendue :
>>> phrase = 'Ceci est une toute petite phrase.'
>>> print(changeCar(phrase, ' ', '*'))
Ceci*est*une*toute*petite*phrase.
>>> print(changeCar(phrase, ' ', '*', 8, 12))
Ceci est*une*toute petite phrase.
>>> print(changeCar(phrase, ' ', '*', 12))
Ceci est une*toute*petite*phrase.
>>> print(changeCar(phrase, ' ', '*', fin = 12))
Ceci*est*une*toute petite phrase.

7.17 Dfinissez une fonction eleMax(liste,debut,fin) qui renvoie llment ayant la plus grande
valeur dans la liste transmise. Les deux arguments debut et fin indiqueront les indices entre
lesquels doit sexercer la recherche, et chacun deux pourra tre omis (comme dans lexercice
prcdent). Exemples de la fonctionnalit attendue :
>>>
>>>
9
>>>
7
>>>
8
>>>
6

serie = [9, 3, 6, 1, 7, 5, 4, 8, 2]
print(eleMax(serie))
print(eleMax(serie, 2, 5))
print(eleMax(serie, 2))
print(eleMax(serie, fin =3, debut =1))

8
Utilisation de fentres et de
graphismes

Jusqu prsent, nous avons utilis Python exclusivement en mode texte . Nous avons procd
ainsi parce quil nous fallait absolument dabord dgager un certain nombre de concepts
lmentaires ainsi que la structure de base du langage, avant denvisager des expriences
impliquant des objets informatiques plus labors (fentres, images, sons, etc.). Nous pouvons
maintenant nous permettre une petite incursion dans le vaste domaine des interfaces graphiques,
mais ce ne sera quun premier amuse-gueule : il nous reste en effet encore bien des choses
fondamentales apprendre, et pour nombre dentre elles lapproche textuelle reste la plus
abordable.

Interfaces graphiques (GUI)


Si vous ne le saviez pas encore, apprenez ds prsent que le domaine des interfaces graphiques (ou
GUI, Graphical User Interfaces) est extrmement complexe. Chaque systme dexploitation peut en effet
proposer plusieurs bibliothques de fonctions graphiques de base, auxquelles viennent
frquemment sajouter de nombreux complments, plus ou moins spcifiques de langages de
programmation particuliers. Tous ces composants sont gnralement prsents comme des classes
dobjets, dont il vous faudra tudier les attributs et les mthodes.
Avec Python, la bibliothque graphique la plus utilise jusqu prsent est la bibliothque tkinter, qui
est une adaptation de la bibliothque Tk dveloppe lorigine pour le langage Tcl. Plusieurs autres
bibliothques graphiques fort intressantes ont t proposes pour Python : wxPython, pyQT, pyGTK,
etc. Il existe galement des possibilits dutiliser les bibliothques de widgets Java et les MFC de Windows.
Dans le cadre de ces notes, nous nous limiterons cependant tkinter, dont il existe fort heureusement
des versions similaires (et gratuites) pour les plates-formes Linux, Windows et Mac OS.

80

Utilisation de fentres et de graphismes

Premiers pas avec tkinter


Pour la suite des explications, nous supposerons bien videmment que le module tkinter37 a dj t
install sur votre systme. Pour pouvoir en utiliser les fonctionnalits dans un script Python, il faut
que lune des premires lignes de ce script contienne linstruction dimportation :
from tkinter import *

Comme toujours sous Python, il nest mme pas ncessaire dcrire un


script. Vous pouvez faire un grand nombre dexpriences directement la
ligne de commande, en ayant simplement lanc Python en mode interactif.
Dans lexemple qui suit, nous allons crer une fentre trs simple, et y
ajouter deux widgets38 typiques : un bout de texte (ou label) et un bouton (ou
button).
>>>
>>>
>>>
>>>
>>>
>>>
>>>

from tkinter import *


fen1 = Tk()
tex1 = Label(fen1, text='Bonjour tout le monde !', fg='red')
tex1.pack()
bou1 = Button(fen1, text='Quitter', command = fen1.destroy)
bou1.pack()
fen1.mainloop()

Suivant la version de Python utilise, vous verrez dj apparatre la fentre dapplication


immdiatement aprs avoir entr la deuxime commande de cet exemple, ou bien
seulement aprs la septime 39.

Examinons prsent plus en dtail chacune des lignes de commandes excutes


1. Comme cela a dj t expliqu prcdemment, il est ais de construire diffrents modules Python,
qui contiendront des scripts, des dfinitions de fonctions, des classes dobjets, etc. On peut alors
importer tout ou partie de ces modules dans nimporte quel programme, ou mme dans linterprteur fonctionnant en mode interactif (cest--dire directement la ligne de commande). Cest ce
que nous faisons la premire ligne de notre exemple : from tkinter import * consiste
importer toutes les classes contenues dans le module tkinter.
Nous devrons de plus en plus souvent parler de ces classes. En programmation, on appelle ainsi des
gnrateurs dobjets, lesquels sont eux-mmes des morceaux de programmes rutilisables. Nous nallons pas essayer de vous fournir ds prsent une dfinition dfinitive et prcise de ce que sont les
objets et les classes, mais plutt vous proposer den utiliser directement quelques-un(e)s. Nous
affinerons notre comprhension petit petit par la suite.
37 Dans

les versions de Python antrieures la version 3.0, le nom de ce module commenait par une
majuscule.
38 widget est le rsultat de la contraction de l expression window gadget . Dans certains environnements
de programmation, on appellera cela plutt un contrle ou un composant graphique . Ce terme dsigne
en fait toute entit susceptible d tre place dans une fentre d application, comme par exemple un bouton,
une case cocher, une image, etc., et parfois aussi la fentre elle-mme.
39 Si vous effectuez cet exercice sous Windows, nous vous conseillons d utiliser de prfrence une version
standard de Python dans une fentre DOS ou dans IDLE plutt que PythonWin. Vous pourrez mieux observer
ce qui se passe aprs lentre de chaque commande.

Premiers pas avec tkinter

81

2. la deuxime ligne de notre exemple : fen1 = Tk(), nous utilisons lune des classes du module
tkinter, la classe Tk(), et nous en crons une instance (autre terme dsignant un objet spcifique),
savoir la fentre fen1.
Ce processus dinstanciation dun objet partir dune classe est une opration fondamentale dans les
techniques actuelles de programmation. Celles-ci font en effet de plus en plus souvent appel une
mthodologie que lon appelle programmation oriente objet (ou OOP : Object Oriented Programming).
La classe est en quelque sorte un modle gnral (ou un moule) partir duquel on demande la
machine de construire un objet informatique particulier. La classe contient toute une srie de
dfinitions et doptions diverses, dont nous nutilisons quune partie dans lobjet que nous crons
partir delle. Ainsi la classe Tk(), qui est lune des classes les plus fondamentales de la bibliothque
tkinter, contient tout ce quil faut pour engendrer diffrents types de fentres dapplication, de
tailles ou de couleurs diverses, avec ou sans barre de menus, etc.
Nous nous en servons ici pour crer notre objet graphique de base, savoir la fentre qui
contiendra tout le reste. Dans les parenthses de Tk(), nous pourrions prciser diffrentes options,
mais nous laisserons cela pour un peu plus tard.
Linstruction dinstanciation ressemble une simple affectation de variable. Comprenons bien
cependant quil se passe ici deux choses la fois :

la cration dun nouvel objet, (lequel peut tre fort complexe dans certains cas, et par consquent
occuper un espace mmoire considrable) ;

laffectation dune variable, qui va dsormais servir de rfrence pour manipuler lobjet40.

3. la troisime ligne :
tex1 = Label(fen1, text='Bonjour tout le monde !', fg='red'),

nous crons un autre objet (un widget), cette fois partir de la classe Label().
Comme son nom lindique, cette classe dfinit toutes sortes dtiquettes (ou de libells). En fait, il
sagit tout simplement de fragments de texte quelconques, utilisables pour afficher des
informations et des messages divers lintrieur dune fentre.
Nous efforant dapprendre au passage la manire correcte dexprimer les choses, nous dirons que
nous crons ici lobjet tex1 par instanciation de la classe Label().
Remarquons que nous faisons appel une classe, de la mme manire que nous faisons appel une
fonction : cest--dire en fournissant un certain nombre darguments dans des parenthses. Nous
verrons plus loin quune classe est en fait une sorte de conteneur dans lequel sont regroupes
des fonctions et des donnes.
Quels arguments avons-nous donc fournis pour cette instanciation ?

Le premier argument transmis (fen1), indique que le nouveau widget que nous sommes en train
de crer sera contenu dans un autre widget prexistant, que nous dsignons donc ici comme son
40 Cette

concision du langage est une consquence du typage dynamique des variables en vigueur sous
Python. Dautres langages utilisent une instruction particulire (telle que new) pour instancier un nouvel
objet. Exemple :
maVoiture = new Cadillac (instanciation dun objet de classe Cadillac, rfrenc dans la variable
maVoiture).

82

Utilisation de fentres et de graphismes


matre : lobjet fen1 est le widget matre de lobjet tex1. On pourra dire aussi que lobjet tex1
est un widget esclave de lobjet fen1.

Les deux arguments suivants servent prciser la forme exacte que doit prendre notre widget.
Ce sont en effet deux options de cration, chacune fournie sous la forme dune chane de
caractres : dabord le texte de ltiquette, ensuite sa couleur davant-plan (ou foreground, en
abrg fg). Ainsi le texte que nous voulons afficher est bien dfini, et il doit apparatre color en
rouge.
Nous pourrions encore prciser bien dautres caractristiques : la police utiliser, ou la couleur
darrire-plan, par exemple. Toutes ces caractristiques ont cependant une valeur par dfaut
dans les dfinitions internes de la classe Label(). Nous ne devons indiquer des options que pour
les caractristiques que nous souhaitons diffrentes du modle standard.

4. la quatrime ligne de notre exemple : tex1.pack(), nous activons une mthode associe lobjet
tex1 : la mthode pack(). Nous avons dj rencontr ce terme de mthode ( propos des listes,
notamment). Une mthode est une fonction intgre un objet (on dira aussi quelle est encapsule
dans lobjet). Nous apprendrons bientt quun objet informatique est en fait un lment de
programme contenant toujours :

un certain nombre de donnes (numriques ou autres), contenues dans des variables de types
divers : on les appelle les attributs (ou les proprits) de lobjet ;

un certain nombre de procdures ou de fonctions (qui sont donc des algorithmes) : on les appelle
les mthodes de lobjet.
La mthode pack() fait partie dun ensemble de mthodes qui sont applicables non seulement
aux widgets de la classe Label(), mais aussi la plupart des autres widgets tkinter, et qui agissent
sur leur disposition gomtrique dans la fentre. Comme vous pouvez le constater par
vous-mme si vous entrez les commandes de notre exemple une par une, la mthode pack()
rduit automatiquement la taille de la fentre matre afin quelle soit juste assez grande pour
contenir les widgets esclaves dfinis au pralable.

5. la cinquime ligne :
bou1 = Button(fen1, text=Quitter, command = fen1.destroy),

nous crons notre second widget esclave : un bouton.


Z

Comme nous lavons fait pour le widget prcdent, nous appelons la classe Button() en fournissant
entre parenthses un certain nombre darguments. tant donn quil sagit cette fois dun objet
interactif, nous devons prciser avec loption command ce qui devra se passer lorsque lutilisateur
effectuera un clic sur le bouton. Dans ce cas prcis, nous actionnerons la mthode destroy associe
lobjet fen1, ce qui devrait provoquer leffacement de la fentre41.
6. La sixime ligne utilise la mthode pack() pour adapter la gomtrie de la fentre au nouvel objet
que nous venons dy intgrer.

41 Attention

: lappel de cette mthode destroy na pas lieu ici (cest--dire dans linstruction dcrivant le
bouton). Il ne faut donc pas accoler de parenthses son nom. Cest tkinter qui se chargera deffectuer lappel
de destroy(), lorsquun utilisateur cliquera sur ce bouton.

Premiers pas avec tkinter

83

7. La septime ligne : fen1.mainloop() est trs importante, parce que cest elle qui provoque le
dmarrage du rceptionnaire dvnements associ la fentre. Cette instruction est ncessaire pour
que notre application soit lafft des clics de souris, des pressions exerces sur les touches du
clavier, etc. Cest donc cette instruction qui la met en marche , en quelque sorte.
Comme son nom lindique (mainloop), il sagit dune mthode de lobjet fen1, qui active une boucle
de programme, laquelle tournera en permanence en tche de fond, dans lattente de messages
mis par le systme dexploitation de lordinateur. Celui-ci interroge en effet sans cesse son
environnement, notamment au niveau des priphriques dentre (souris, clavier, etc.). Lorsquun
vnement quelconque est dtect, divers messages dcrivant cet vnement sont expdis aux
programmes qui souhaitent en tre avertis. Voyons cela un peu plus en dtail.

Programmes pilots par des vnements


Vous venez dexprimenter votre premier programme utilisant une interface graphique. Ce type de
programme est structur dune manire diffrente des scripts textuels tudis auparavant.
Tous les programmes dordinateur comportent grosso-modo trois phases
principales : une phase dinitialisation, laquelle contient les instructions qui
prparent le travail effectuer (appel des modules externes ncessaires,
ouverture de fichiers, connexion un serveur de bases de donnes ou lInternet, etc.), une phase centrale o lon trouve la vritable fonctionnalit du
programme (cest--dire tout ce quil est cens faire : afficher des donnes
lcran, effectuer des calculs, modifier le contenu dun fichier, imprimer, etc.), et
enfin une phase de terminaison qui sert clturer proprement les oprations
(cest--dire fermer les fichiers rests ouverts, couper les connexions externes,
etc.).
Dans un programme en mode texte , ces trois phases sont simplement
organises suivant un schma linaire comme dans lillustration ci-contre. En
consquence, ces programmes se caractrisent par une interactivit trs limite
avec lutilisateur. Celui-ci ne dispose pratiquement daucune libert : il lui est
demand de temps autre dentrer des donnes au clavier, mais toujours dans
un ordre prdtermin correspondant la squence dinstructions du
programme.
Dans le cas dun programme qui utilise une interface graphique, par contre, lorganisation interne est
diffrente. On dit dun tel programme quil est pilot par les vnements. Aprs sa phase dinitialisation,
un programme de ce type se met en quelque sorte en attente , et passe la main un autre logiciel,
lequel est plus ou moins intimement intgr au systme dexploitation de lordinateur et tourne en
permanence.
Ce rceptionnaire dvnements scrute sans cesse tous les priphriques (clavier, souris, horloge, modem,
etc.) et ragit immdiatement lorsquun vnement y est dtect.
Un tel vnement peut tre une action quelconque de lutilisateur : dplacement de la souris, appui
sur une touche, etc., mais aussi un vnement externe ou un automatisme (top dhorloge, par
exemple).

84

Utilisation de fentres et de graphismes

Lorsquil dtecte un vnement, le rceptionnaire envoie un message spcifique au programme42,


lequel doit tre conu pour ragir en consquence.
La phase dinitialisation dun programme utilisant une interface graphique comporte un ensemble
dinstructions qui mettent en place les divers composants interactifs de cette interface (fentres,
boutons, cases cocher, etc.). Dautres instructions dfinissent les messages dvnements qui devront
tre pris en charge : on peut en effet dcider que le programme ne ragira qu certains vnements
en ignorant tous les autres.
Alors que dans un programme textuel , la phase centrale est constitue dune suite dinstructions
qui dcrivent lavance lordre dans lequel la machine devra excuter ses diffrentes tches (mme sil
est prvu des cheminements diffrents en rponse certaines conditions rencontres en cours de
route), on ne trouve dans la phase centrale dun programme avec interface graphique quun ensemble
de fonctions indpendantes. Chacune de ces fonctions est appele spcifiquement lorsquun
vnement particulier est dtect par le systme dexploitation : elle effectue alors le travail que lon
attend du programme en rponse cet vnement, et rien dautre43.
Il est important de bien comprendre ici que pendant tout ce temps, le rceptionnaire continue
tourner et guetter lapparition dautres vnements ventuels.
Sil arrive dautres vnements, il peut donc se faire quune deuxime fonction (ou une 3e, une 4e...)
soit active et commence effectuer son travail en parallle avec la premire qui na pas encore
42 Ces

messages sont souvent nots WM (Window messages) dans un environnement graphique constitu de
fentres (avec de nombreuses zones ractives : boutons, cases cocher, menus droulants, etc.). Dans la
description des algorithmes, il arrive frquemment aussi qu on confonde ces messages avec les vnements
eux-mmes.
43 Au sens strict, une telle fonction qui ne devra renvoyer aucune valeur est donc plutt une procdure (cf.
page 68).

Programmes pilots par des vnements

85

termin le sien44. Les systmes dexploitation et les langages modernes permettent en effet ce
paralllisme que lon appelle aussi multitche.
Au chapitre prcdent, nous avons dj remarqu que la structure du texte dun programme nindique
pas directement lordre dans lequel les instructions seront finalement excutes. Cette remarque sapplique encore bien davantage dans le cas dun programme avec interface graphique, puisque lordre
dans lequel les fonctions sont appeles nest plus inscrit nulle part dans le programme. Ce sont les
vnements qui pilotent !
Tout ceci doit vous paratre un peu compliqu. Nous allons lillustrer dans quelques exemples.

Exemple graphique : trac de lignes dans un canevas


Le script dcrit ci-dessous cre une fentre
comportant trois boutons et un canevas. Suivant la
terminologie de tkinter, un canevas est une surface
rectangulaire dlimite, dans laquelle on peut
installer ensuite divers dessins et images laide de
mthodes spcifiques45.
Lorsque lon clique sur le bouton <Tracer une
ligne>, une nouvelle ligne colore apparat sur le
canevas, avec chaque fois une inclinaison
diffrente de la prcdente.
Si lon actionne le bouton <Autre couleur>, une
nouvelle couleur est tire au hasard dans une srie
limite. Cette couleur est celle qui sappliquera aux
tracs suivants.
Le bouton <Quitter> sert bien videmment terminer lapplication en refermant la fentre.
# Petit exercice utilisant la bibliothque graphique tkinter
from tkinter import *
from random import randrange
# --- dfinition des fonctions gestionnaires d'vnements : --def drawline():
"Trac d'une ligne dans le canevas can1"
global x1, y1, x2, y2, coul
can1.create_line(x1,y1,x2,y2,width=2,fill=coul)
# modification des coordonnes pour la ligne suivante :
y2, y1 = y2+10, y1-10
def changecolor():
"Changement alatoire de la couleur du trac"
global coul
44 En

particulier, la mme fonction peut tre appele plusieurs fois en rponse l occurrence de quelques
vnements identiques, la mme tche tant alors effectue en plusieurs exemplaires concurrents. Nous
verrons plus loin quil peut en rsulter des effets de bord gnants.
45 Ces dessins pourront ventuellement tre anims dans une phase ultrieure.

86

Utilisation de fentres et de graphismes


pal=['purple','cyan','maroon','green','red','blue','orange','yellow']
c = randrange(8)
# => gnre un nombre alatoire de 0 7
coul = pal[c]

#------ Programme principal ------# les variables suivantes seront utilises de manire globale :
x1, y1, x2, y2 = 10, 190, 190, 10
# coordonnes de la ligne
coul = 'dark green'
# couleur de la ligne
# Cration du widget principal ("matre") :
fen1 = Tk()
# cration des widgets "esclaves" :
can1 = Canvas(fen1,bg='dark grey',height=200,width=200)
can1.pack(side=LEFT)
bou1 = Button(fen1,text='Quitter',command=fen1.quit)
bou1.pack(side=BOTTOM)
bou2 = Button(fen1,text='Tracer une ligne',command=drawline)
bou2.pack()
bou3 = Button(fen1,text='Autre couleur',command=changecolor)
bou3.pack()
fen1.mainloop()

# dmarrage du rceptionnaire dvnements

fen1.destroy()

# destruction (fermeture) de la fentre

Conformment ce que nous avons expliqu dans le texte des pages prcdentes, la fonctionnalit de
ce programme est essentiellement assure par les deux fonctions drawline() et changecolor(), qui
seront actives par des vnements, ceux-ci tant eux-mmes dfinis dans la phase dinitialisation.
Dans cette phase dinitialisation, on commence par importer lintgralit du module tkinter ainsi
quune fonction du module random qui permet de tirer des nombres au hasard. On cre ensuite les
diffrents widgets par instanciation partir des classes Tk(), Canvas() et Button(). Remarquons au
passage que la mme classe Button() sert instancier plusieurs boutons, qui sont des objets similaires
pour lessentiel, mais nanmoins individualiss grce aux options de cration et qui pourront
fonctionner indpendamment lun de lautre.
Linitialisation se termine avec linstruction fen1.mainloop() qui dmarre le rceptionnaire dvnements. Les instructions qui suivent ne seront excutes qu la sortie de cette boucle, sortie elle-mme
dclenche par la mthode fen1.quit() (voir ci-aprs).
Loption command utilise dans linstruction dinstanciation des boutons permet de dsigner la
fonction qui devra tre appele lorsquun vnement clic gauche de la souris sur le widget se produira.
Il sagit en fait dun raccourci pour cet vnement particulier, qui vous est propos par tkinter pour
votre facilit parce que cet vnement est celui que lon associe naturellement un widget de type
bouton. Nous verrons plus loin quil existe dautres techniques plus gnrales pour associer nimporte
quel type dvnement nimporte quel widget.
Les fonctions de ce script peuvent modifier les valeurs de certaines variables qui ont t dfinies au
niveau principal du programme. Cela est rendu possible grce linstruction global utilise dans la
dfinition de ces fonctions. Nous nous permettrons de procder ainsi pendant quelque temps encore
(ne serait-ce que pour vous habituer distinguer les comportements des variables locales et globales),
mais comme vous le comprendrez plus loin, cette pratique nest pas vraiment recommandable, surtout lors-

Programmes pilots par des vnements

87

quil sagit dcrire de grands programmes. Nous apprendrons une meilleure technique lorsque nous
aborderons ltude des classes ( partir de la page 163).
Dans notre fonction changecolor(), une couleur est choisie au hasard dans une liste. Nous utilisons
pour ce faire la fonction randrange() importe du module random. Appele avec un argument N, cette
fonction renvoie un nombre entier, tir au hasard entre 0 et N-1.
La commande lie au bouton <Quitter> appelle la mthode quit() de la fentre fen1. Cette mthode
sert fermer (quitter) le rceptionnaire dvnements (mainloop) associ cette fentre. Lorsque
cette mthode est active, lexcution du programme se poursuit avec les instructions qui suivent lappel de mainloop. Dans notre exemple, cela consiste donc effacer (destroy) la fentre.
Exercices
8.1 Comment faut-il modifier le programme pour ne plus avoir que des lignes de couleur cyan,
maroon et green ?

8.2

Comment modifier le programme pour que toutes les lignes traces soient horizontales et
parallles ?

8.3

Agrandissez le canevas de manire lui donner une largeur de 500 units et une hauteur de
650. Modifiez galement la taille des lignes, afin que leurs extrmits se confondent avec les
bords du canevas.

8.4

Ajoutez une fonction drawli ne2 qui tracera deux lignes rouges en croix au centre du
canevas : lune horizontale et lautre verticale. Ajoutez galement un bouton portant lindication viseur . Un clic sur ce bouton devra provoquer laffichage de la croix.

8.5

Reprenez le programme initial. Remplacez la mthode create_line par create_rectangle. Que


se passe-t-il ?
De la mme faon, essayez aussi create_arc, create_oval, et create_polygon.
Pour chacune de ces mthodes, notez ce quindiquent les coordonnes fournies en paramtres.
(Remarque : pour le polygone, il est ncessaire de modifier lgrement le programme !)

8.6

- Supprimez la ligne global x1, y1, x2, y2 dans la fonction drawline du programme original.
Que se passe-t-il ? Pourquoi ?
- Si vous placez plutt x1, y1, x2, y2 entre les parenthses, dans la ligne de dfinition de la
fonction drawline, de manire transmettre ces variables la fonction en tant que
paramtres, le programme fonctionne-t-il encore ? Noubliez pas de modifier aussi la ligne du
programme qui fait appel cette fonction !
- Si vous dfinissez x1, y1, x2, y2 = 10, 390, 390, 10 la place de global x1, y1..., que se
passe-t-il ? Pourquoi ? Quelle conclusion pouvez-vous tirer de tout cela ?

8.7

a) Crez un court programme qui dessinera les 5 anneaux olympiques dans un rectangle de
fond blanc (white). Un bouton <Quitter> doit permettre de fermer la fentre.
b) Modifiez le programme ci-dessus en y ajoutant 5 boutons. Chacun de ces boutons
provoquera le trac de chacun des 5 anneaux

8.8

Dans votre cahier de notes, tablissez un tableau deux colonnes. Vous y noterez gauche les
dfinitions des classes dobjets dj rencontres (avec leur liste de paramtres), et droite les

88

Utilisation de fentres et de graphismes


mthodes associes ces classes (galement avec leurs paramtres). Laissez de la place pour
complter ultrieurement.

Exemple graphique : deux dessins alterns


Cet autre exemple vous montrera comment vous pouvez exploiter les connaissances que vous avez
acquises prcdemment, concernant les boucles, les listes et les fonctions, afin de raliser de
nombreux dessins avec seulement quelques lignes de code. Il sagit dune petite application qui affiche
lun ou lautre des deux dessins reproduits ci-dessous, en fonction du bouton choisi :

from tkinter import *


def cercle(x, y, r, coul ='black'):
"trac d'un cercle de centre (x,y) et de rayon r"
can.create_oval(x-r, y-r, x+r, y+r, outline=coul)
def figure_1():
"dessiner une cible"
# Effacer d'abord tout dessin prexistant :
can.delete(ALL)
# tracer les deux lignes (vert. Et horiz.) :
can.create_line(100, 0, 100, 200, fill ='blue')
can.create_line(0, 100, 200, 100, fill ='blue')
# tracer plusieurs cercles concentriques :
rayon = 15
while rayon < 100:
cercle(100, 100, rayon)
rayon += 15
def figure_2():
"dessiner un visage simplifi"
# Effacer d'abord tout dessin prexistant :
can.delete(ALL)
# Les caractristiques de chaque cercle sont
# places dans une liste de listes :
cc =[[100, 100, 80, 'red'],
# visage
[70, 70, 15, 'blue'],
# yeux
[130, 70, 15, 'blue'],
[70, 70, 5, 'black'],
[130, 70, 5, 'black'],

Programmes pilots par des vnements

89

[44, 115, 20, 'red'],


# joues
[156, 115, 20, 'red'],
[100, 95, 15, 'purple'],
# nez
[100, 145, 30, 'purple']] # bouche
# on trace tous les cercles l'aide d'une boucle :
i =0
while i < len(cc):
# parcours de la liste
el = cc[i]
# chaque lment est lui-mme une liste
cercle(el[0], el[1], el[2], el[3])
i += 1
##### Programme principal : ############
fen = Tk()
can = Canvas(fen, width =200, height =200, bg ='ivory')
can.pack(side =TOP, padx =5, pady =5)
b1 = Button(fen, text ='dessin 1', command =figure_1)
b1.pack(side =LEFT, padx =3, pady =3)
b2 = Button(fen, text ='dessin 2', command =figure_2)
b2.pack(side =RIGHT, padx =3, pady =3)
fen.mainloop()

Commenons par analyser le programme principal, la fin du script :


Nous y crons une fentre, par instanciation dun objet de la classe Tk() dans la variable fen.
Ensuite, nous installons 3 widgets dans cette fentre : un canevas et deux boutons. Le canevas est
instanci dans la variable can, et les deux boutons dans les variables b1 et b2. Comme dans le script
prcdent, les widgets sont mis en place dans la fentre laide de leur mthode pack(), mais cette fois
nous utilisons celle-ci avec des options :

loption side peut accepter les valeurs TOP, BOTTOM, LEFT ou RIGHT, pour pousser le widget
du ct correspondant dans la fentre. Ces noms crits en majuscules sont en fait ceux dune srie
de variables importes avec le module tkinter, et que vous pouvez considrer comme des pseudo-constantes .
les options padx et pady permettent de rserver un petit espace autour du widget. Cet espace est
exprim en nombre de pixels : padx rserve un espace gauche et droite du widget, pady
rserve un espace au-dessus et au-dessous du widget.
Les boutons commandent laffichage des deux dessins, en invoquant les fonctions figure_1() et figure_2(). Considrant que nous aurions tracer un certain nombre de cercles dans ces dessins, nous
avons estim quil serait bien utile de dfinir dabord une fonction cercle() spcialise. En effet, vous
savez probablement dj que le canevas tkinter est dot dune mthode create_oval() qui permet de
dessiner des ellipses quelconques (et donc aussi des cercles), mais cette mthode doit tre invoque
avec quatre arguments qui seront les coordonnes des coins suprieur gauche et infrieur droit dun
rectangle fictif, dans lequel lellipse viendra alors sinscrire. Cela nest pas trs pratique dans le cas
particulier du cercle : il nous semblera plus naturel de commander ce trac en fournissant les
coordonnes de son centre ainsi que son rayon. Cest ce que nous obtiendrons avec notre fonction
cercle(), laquelle invoque la mthode create_oval() en effectuant la conversion des coordonnes.
Remarquez aussi que cette fonction attend un argument facultatif en ce qui concerne la couleur du
cercle tracer (noir par dfaut).
Lefficacit de cette approche apparat clairement dans la fonction figure_1(), ou nous trouvons une
simple boucle de rptition pour dessiner toute la srie de cercles (de mme centre et de rayon crois-

90

Utilisation de fentres et de graphismes

sant). Notez au passage lutilisation de loprateur += qui permet dincrmenter une variable (dans
notre exemple, la variable r voit sa valeur augmenter de 15 units chaque itration).
Le second dessin est un peu plus complexe, parce quil est compos de cercles de tailles varies centrs
sur des points diffrents. Nous pouvons tout de mme tracer tous ces cercles laide dune seule
boucle de rptition, si nous mettons profit nos connaissances concernant les listes.
En effet, ce qui diffrencie les cercles que nous voulons tracer tient en quatre caractristiques :
coordonnes x et y du centre, rayon et couleur. Pour chaque cercle, nous pouvons placer ces quatre
caractristiques dans une petite liste, et rassembler toutes les petites listes ainsi obtenues dans une
autre liste plus grande. Nous disposerons ainsi dune liste de listes, quil suffira ensuite de parcourir
laide dune boucle pour effectuer les tracs correspondants.
Exercices
8.9 Inspirez-vous du script prcdent pour crire une petite
application qui fait apparatre un damier (dessin de cases noires
sur fond blanc) lorsque lon clique sur un bouton :

8.10 lapplication de lexercice prcdent, ajoutez un bouton qui


fera apparatre des pions au hasard sur le damier (chaque
pression sur le bouton fera apparatre un nouveau pion).

Exemple graphique : calculatrice minimaliste


Bien que trs court, le petit script ci-dessous implmente une
calculatrice complte, avec laquelle vous pourrez mme effectuer des
calculs comportant des parenthses et des fonctions scientifiques. Ny
voyez rien dextraordinaire. Toute cette fonctionnalit nest quune
consquence du fait que vous utilisez un interprteur plutt quun
compilateur pour excuter vos programmes.
Comme vous le savez, le compilateur nintervient quune seule fois, pour traduire lensemble de votre
code source en un programme excutable. Son rle est donc termin avant mme lexcution du
programme. Linterprteur, quant lui, est toujours actif pendant lexcution du programme, et donc
tout fait disponible pour traduire un nouveau code source quelconque, comme une expression
mathmatique entre au clavier par lutilisateur.
Les langages interprts disposent donc toujours de fonctions permettant dvaluer une chane de
caractres comme une suite dinstructions du langage lui-mme. Il devient alors possible de construire
en peu de lignes des structures de programmes trs dynamiques. Dans lexemple ci-dessous, nous
utilisons la fonction intgre eval() pour analyser lexpression mathmatique entre par lutilisateur
dans le champ prvu cet effet, et nous navons plus ensuite qu afficher le rsultat.

Programmes pilots par des vnements

91

# Exercice utilisant la bibliothque graphique tkinter et le module math


from tkinter import *
from math import *
# dfinition de l'action effectuer si l'utilisateur actionne
# la touche "enter" alors qu'il dite le champ d'entre :
def evaluer(event):
chaine.configure(text = "Rsultat = " + str(eval(entree.get())))
# ----- Programme principal : ----fenetre = Tk()
entree = Entry(fenetre)
entree.bind("<Return>", evaluer)
chaine = Label(fenetre)
entree.pack()
chaine.pack()
fenetre.mainloop()

Au dbut du script, nous commenons par importer les modules tkinter et math, ce dernier tant
ncessaire afin que la dite calculatrice puisse disposer de toutes les fonctions mathmatiques et
scientifiques usuelles : sinus, cosinus, racine carre, etc.
Ensuite nous dfinissons une fonction evaluer(), qui sera en fait la commande excute par le
programme lorsque lutilisateur actionnera la touche Return (ou Enter) aprs avoir entr une
expression mathmatique quelconque dans le champ dentre dcrit plus loin.
Cette fonction utilise la mthode configure() du widget chaine46, pour modifier son attribut text. Lattribut en question reoit donc ici une nouvelle valeur, dtermine par ce que nous avons crit la
droite du signe gale : il sagit en loccurrence dune chane de caractres construite dynamiquement,
laide de deux fonctions intgres dans Python : eval() et str(), et dune mthode associe un
widget tkinter : la mthode get().
eval() fait appel linterprteur pour valuer une expression Python qui lui est transmise dans une
chane de caractres. Le rsultat de lvaluation est fourni en retour. Exemple :
chaine = "(25 + 8)/3"
res = eval(chaine)
print(res +5)

# chane contenant une expression mathmatique


# valuation de l'expression contenue dans la chane
# => le contenu de la variable res est numrique

str() transforme une expression numrique en chane de caractres. Nous devons faire appel cette
fonction parce que la prcdente renvoie une valeur numrique, que nous convertissons nouveau en
chane de caractres pour pouvoir lincorporer au message Rsultat =.
get() est une mthode associe aux widgets de la classe Entry. Dans notre petit programme exemple,
nous utilisons un widget de ce type pour permettre lutilisateur dentrer une expression numrique
quelconque laide de son clavier. La mthode get() permet en quelque sorte dextraire du widget
entree la chane de caractres qui lui a t fournie par lutilisateur.

46 La

mthode
proprits.

configure()

peut sappliquer nimporte quel widget prexistant, pour en modifier les

92

Utilisation de fentres et de graphismes

Le corps du programme principal contient la phase dinitialisation, qui se termine par la mise en route
de lobservateur dvnements (mainloop). On y trouve linstanciation dune fentre Tk(), contenant
un widget chaine instanci partir de la classe Label(), et un widget entree instanci partir de la
classe Entry().
Attention : afin que ce dernier widget puisse vraiment faire son travail, cest--dire transmettre au
programme lexpression que lutilisateur y aura encode, nous lui associons un vnement laide de
la mthode bind()47 :
entree.bind("<Return>",evaluer)

Cette instruction signifie : Lier lvnement <pression sur la touche Return> lobjet <entree>, le gestionnaire
de cet vnement tant la fonction <evaluer> .
Lvnement prendre en charge est dcrit dans une chane de caractres spcifique (dans notre
exemple, il sagit de la chane "<Return>". Il existe un grand nombre de ces vnements (mouvements
et clics de la souris, enfoncement des touches du clavier, positionnement et redimensionnement des
fentres, passage au premier plan, etc.). Vous trouverez la liste des chanes spcifiques de tous ces
vnements dans les ouvrages de rfrence traitant de tkinter.
Remarquez bien quil ny a pas de parenthses aprs le nom de la fonction evaluer. En effet : dans
cette instruction, nous ne souhaitons pas dj invoquer la fonction elle-mme (ce serait prmatur) ;
ce que nous voulons, cest tablir un lien entre un type dvnement particulier et cette fonction, de
manire ce quelle soit invoque plus tard, chaque fois que lvnement se produira. Si nous mettions
des parenthses, largument qui serait transmis la mthode bind() serait la valeur de retour de cette
fonction et non sa rfrence.
Profitons aussi de loccasion pour observer encore une fois la syntaxe des instructions destines
mettre en uvre une mthode associe un objet :
objet.mthode(arguments)
On crit dabord le nom de lobjet sur lequel on dsire intervenir, puis le point (qui fait office doprateur), puis le nom de la mthode mettre en uvre ; entre les parenthses associes cette mthode,
on indique enfin les arguments quon souhaite lui transmettre.

Exemple graphique : dtection et positionnement dun clic de souris


Dans la dfinition de la fonction evaluer de lexemple prcdent, vous aurez remarqu que nous
avons fourni un argument event (entre les parenthses).
Cet argument est obligatoire48. Lorsque vous dfinissez une fonction gestionnaire dvnement qui est
associe un widget quelconque laide de sa mthode bind(), vous devez toujours lutiliser comme
premier argument. Cet argument dsigne en effet un objet cr automatiquement par tkinter, qui
permet de transmettre au gestionnaire dvnement un certain nombre dattributs de lvnement :

47 En anglais, le mot bind signifie lier .


48 La prsence dun argument est obligatoire,

mais le nom event est une simple convention. Vous pourriez


utiliser un autre nom quelconque sa place, bien que cela ne soit pas recommand.

Programmes pilots par des vnements

93

le type dvnement : dplacement de la souris, enfoncement ou relchement de lun de ses


boutons, appui sur une touche du clavier, entre du curseur dans une zone prdfinie, ouverture
ou fermeture dune fentre, etc.
une srie de proprits de lvnement : linstant o il sest produit, ses coordonnes, les
caractristiques du ou des widget(s) concern(s), etc.
Nous nallons pas entrer dans trop de dtails. Si vous voulez bien encoder et exprimenter le petit
script ci-dessous, vous aurez vite compris le principe.
# Dtection et positionnement d'un clic de souris dans une fentre :
from tkinter import *
def pointeur(event):
chaine.configure(text = "Clic dtect en X =" + str(event.x) +\
", Y =" + str(event.y))
fen = Tk()
cadre = Frame(fen, width =200, height =150, bg="light yellow")
cadre.bind("<Button-1>", pointeur)
cadre.pack()
chaine = Label(fen)
chaine.pack()
fen.mainloop()

Le script fait apparatre une fentre contenant un cadre (Frame)


rectangulaire de couleur jaune ple, dans lequel lutilisateur est
invit effectuer des clics de souris.
La mthode bind() du widget cadre associe lvnement <clic
laide du premier bouton de la souris> au gestionnaire dvnement
pointeur .
Ce gestionnaire dvnement peut utiliser les attributs x et y de
lobjet event gnr automatiquement par tkinter, pour
construire la chane de caractres qui affichera la position de la
souris au moment du clic.
Exercice
8.11 Modifiez le script ci-dessus de manire faire apparatre un petit cercle rouge lendroit o
lutilisateur a effectu son clic (vous devrez dabord remplacer le widget Frame par un widget
Canvas).

Les classes de widgets tkinter


Note
Tout au long de cet ouvrage, nous vous prsenterons petit petit le mode d utilisation
dun certain nombre de widgets. Comprenez bien cependant quil nentre pas dans nos
intentions de fournir ici un manuel de rfrence complet sur tkinter. Nous limiterons nos
explications aux widgets qui nous semblent les plus intressants dun point de vue
didactique, cest--dire ceux qui pourront nous aider mettre en vidence des concepts

94

Utilisation de fentres et de graphismes


de programmation importants, tels ceux de classe et dobjet. Veuillez donc consulter la
littrature (voir page 10) si vous souhaitez davantage de prcisions.

Il existe 15 classes de base pour les widgets tkinter :


Widget

Description

Button

Un bouton classique, utiliser pour provoquer lexcution dune commande


quelconque.

Canvas

Un espace pour disposer divers lments graphiques. Ce widget peut tre


utilis pour dessiner, crer des diteurs graphiques, et aussi pour implmenter
des widgets personnaliss.

Checkbutton

Une case cocher qui peut prendre deux tats distincts (la case est coche ou
non). Un clic sur ce widget provoque le changement dtat.

Entry

Un champ dentre, dans lequel lutilisateur du programme pourra insrer un


texte quelconque partir du clavier.

Frame

Une surface rectangulaire dans la fentre, o lon peut disposer dautres


widgets. Cette surface peut tre colore. Elle peut aussi tre dcore dune
bordure.

Label

Un texte (ou libell) quelconque (ventuellement une image).

Listbox

Une liste de choix proposs lutilisateur, gnralement prsents dans une


sorte de bote. On peut galement configurer la Listbox de telle manire
quelle se comporte comme une srie de boutons radio ou de cases
cocher.

Menu

Un menu. Ce peut tre un menu droulant attach la barre de titre, ou bien


un menu pop up apparaissant nimporte o la suite dun clic.

Menubutton

Un bouton-menu, utiliser pour implmenter des menus droulants.

Message

Permet dafficher un texte. Ce widget est une variante du widget Label, qui
permet dadapter automatiquement le texte affich une certaine taille ou
un certain rapport largeur/hauteur.

Radiobutton

Reprsente (par un point noir dans un petit cercle) une des valeurs dune
variable qui peut en possder plusieurs. Cliquer sur un bouton radio donne la
valeur correspondante la variable, et vide tous les autres boutons radio
associs la mme variable.

Scale

Vous permet de faire varier de manire trs visuelle la valeur dune variable,
en dplaant un curseur le long dune rgle.

Scrollbar

Ascenseur ou barre de dfilement que vous pouvez utiliser en association avec


les autres widgets : Canvas, Entry, Listbox, Text.

Text

Affichage de texte format. Permet aussi lutilisateur dditer le texte affich.


Des images peuvent galement tre insres.

Toplevel

Une fentre affiche sparment, au premier plan.

Ces classes de widgets intgrent chacune un grand nombre de mthodes. On peut aussi leur associer
(lier) des vnements, comme nous venons de le voir dans les pages prcdentes. Vous allez apprendre
en outre que tous ces widgets peuvent tre positionns dans les fentres laide de trois mthodes
diffrentes : la mthode grid(), la mthode pack() et la mthode place().
Lutilit de ces mthodes apparat clairement lorsque lon sefforce de raliser des programmes
portables (cest--dire susceptibles de fonctionner de manire identique sur des systmes dexploitation aussi diffrents que Unix, Mac OS ou Windows), et dont les fentres soient redimensionnables.

Utilisation de la mthode grid pour contrler la disposition des widgets

95

Utilisation de la mthode grid pour contrler la disposition des widgets


Jusqu prsent, nous avons toujours dispos les
widgets dans leur fentre laide de la mthode pack().
Cette mthode prsentait lavantage dtre
extraordinairement simple, mais elle ne nous donnait
pas beaucoup de libert pour disposer les widgets
notre guise. Comment faire, par exemple, pour obtenir la fentre ci-contre ?
Nous pourrions effectuer un certain nombre de tentatives en fournissant la mthode pack() des
arguments de type side = , comme nous lavons dj fait prcdemment, mais cela ne nous mne pas
trs loin. Essayons par exemple :
from tkinter import *
fen1 = Tk()
txt1 = Label(fen1, text = 'Premier champ :')
txt2 = Label(fen1, text = 'Second :')
entr1 = Entry(fen1)
entr2 = Entry(fen1)
txt1.pack(side =LEFT)
txt2.pack(side =LEFT)
entr1.pack(side =RIGHT)
entr2.pack(side =RIGHT)
fen1.mainloop()

... mais le rsultat nest pas vraiment celui que nous recherchions !

Pour mieux comprendre comment fonctionne la mthode pack(), vous pouvez encore essayer
diffrentes combinaisons doptions, telles que side =TOP, side =BOTTOM, pour chacun de ces quatre
widgets. Mais vous narriverez certainement pas obtenir ce qui vous a t demand. Vous pourriez
peut-tre y parvenir en dfinissant deux widgets Frame() supplmentaires, et en y incorporant
ensuite sparment les widgets Label() et Entry(). Cela devient fort compliqu.
Il est temps que nous apprenions utiliser une autre approche du problme. Veuillez donc analyser le
script ci-dessous : il contient en effet (presque) la solution :
from tkinter import *
fen1 = Tk()
txt1 = Label(fen1, text =
txt2 = Label(fen1, text =
entr1 = Entry(fen1)
entr2 = Entry(fen1)
txt1.grid(row =0)
txt2.grid(row =1)
entr1.grid(row =0, column
entr2.grid(row =1, column
fen1.mainloop()

'Premier champ :')


'Second :')

=1)
=1)

96

Utilisation de fentres et de graphismes

Dans ce script, nous avons donc remplac la mthode pack() par la mthode grid(). Comme vous
pouvez le constater, lutilisation de la mthode grid() est trs simple. Cette mthode considre la
fentre comme un tableau (ou une grille). Il suffit alors de lui indiquer dans quelle ligne (row) et dans
quelle colonne (column) de ce tableau on souhaite placer les widgets. On peut numroter les lignes et
les colonnes comme on veut, en partant de zro, ou de un, ou encore dun nombre quelconque : tkinter
ignorera les lignes et colonnes vides. Notez cependant que si vous ne fournissez aucun numro pour
une ligne ou une colonne, la valeur par dfaut sera zro.
Tkinter dtermine automatiquement le nombre de lignes et de colonnes ncessaire. Mais ce nest pas
tout : si vous examinez en dtail la petite fentre produite par le script ci-dessus, vous constaterez que
nous navons pas encore tout fait atteint le but poursuivi. Les deux chanes apparaissant dans la
partie gauche de la fentre sont centres, alors que nous souhaitions les aligner lune et lautre par la
droite. Pour obtenir ce rsultat, il nous suffit dajouter un argument dans lappel de la mthode grid()
utilise pour ces widgets. Loption sticky peut prendre lune des quatre valeurs N, S, W, E (les quatre
points cardinaux en anglais). En fonction de cette valeur, on obtiendra un alignement des widgets par
le haut, par le bas, par la gauche ou par la droite. Remplacez donc les deux premires instructions
grid() du script par :
txt1.grid(row =0, sticky =E)
txt2.grid(row =1, sticky =E)

... et vous atteindrez enfin exactement le but recherch.


Analysons prsent la fentre suivante :

Cette fentre comporte 3 colonnes : une premire avec les 3 chanes de caractres, une seconde avec
les 3 champs dentre, et une troisime avec limage. Les deux premires colonnes comportent
chacune 3 lignes, mais limage situe dans la dernire colonne stale en quelque sorte sur les trois.
Le code correspondant est le suivant :
from tkinter import *
fen1 = Tk()
# cration de widgets 'Label' et 'Entry' :

Utilisation de la mthode grid pour contrler la disposition des widgets

97

txt1 = Label(fen1, text ='Premier champ :')


txt2 = Label(fen1, text ='Second :')
txt3 = Label(fen1, text ='Troisime :')
entr1 = Entry(fen1)
entr2 = Entry(fen1)
entr3 = Entry(fen1)
# cration d'un widget 'Canvas' contenant une image bitmap :
can1 = Canvas(fen1, width =160, height =160, bg ='white')
photo = PhotoImage(file ='martin_p.gif')
item = can1.create_image(80, 80, image =photo)
# Mise en page l'aide de la mthode 'grid' :
txt1.grid(row =1, sticky =E)
txt2.grid(row =2, sticky =E)
txt3.grid(row =3, sticky =E)
entr1.grid(row =1, column =2)
entr2.grid(row =2, column =2)
entr3.grid(row =3, column =2)
can1.grid(row =1, column =3, rowspan =3, padx =10, pady =5)
# dmarrage :
fen1.mainloop()

Pour pouvoir faire fonctionner ce script, il vous faudra probablement remplacer le nom du fichier
image (martin_p.gif) par le nom dune image de votre choix. Attention : la bibliothque tkinter standard
naccepte quun petit nombre de formats pour cette image. Choisissez de prfrence le format GIF 49.
Nous pouvons remarquer un certain nombre de choses dans ce script :
1. La technique utilise pour incorporer une image :
tkinter ne permet pas dinsrer directement une image dans une fentre. Il faut dabord installer
un canevas, et ensuite positionner limage dans celui-ci. Nous avons opt pour un canevas de
couleur blanche, afin de pouvoir le distinguer de la fentre. Vous pouvez remplacer le paramtre
bg ='white' par bg ='gray' si vous souhaitez que le canevas devienne invisible. tant donn quil
existe de nombreux types dimages, nous devons en outre dclarer lobjet image comme tant un
bitmap GIF, partir de la classe PhotoImage().
2. La ligne o nous installons limage dans le canevas est la ligne :
item = can1.create_image(80, 80, image =photo)

Pour employer un vocabulaire correct, nous dirons que nous utilisons ici la mthode
create_image() associe lobjet can1 (lequel objet est lui-mme une instance de la classe Canvas).
Les deux premiers arguments transmis (80, 80) indiquent les coordonnes x et y du canevas o il
faut placer le centre de limage. Les dimensions du canevas tant de 160x160, notre choix aboutira
donc un centrage de limage au milieu du canevas.
3. La numrotation des lignes et colonnes dans la mthode grid() :
On peut constater que la numrotation des lignes et des colonnes dans la mthode grid() utilise ici
commence cette fois partir de 1 (et non partir de zro comme dans le script prcdent). Comme
nous lavons dj signal plus haut, ce choix de numrotation est tout fait libre.
49 Dautres

formats dimage sont possibles, mais la condition de les traiter l aide des modules graphiques
de la bibliothque PIL (Python Imaging Library), qui est une extension de Python disponible sur :
http://www.pythonware.com/products/pil/. Cette bibliothque permet en outre d effectuer une multitude de
traitements divers sur des images, mais l tude de ces techniques dpasse largement le cadre que nous nous
sommes fixs pour ce manuel.

98

Utilisation de fentres et de graphismes


On pourrait tout aussi bien numroter : 5, 10, 15, 20... puisque tkinter ignore les lignes et les
colonnes vides. Numroter partir de l augmente probablement la lisibilit de notre code.

4. Les arguments utiliss avec grid() pour positionner le canevas :


can1.grid(row =1, column =3, rowspan =3, padx =10, pady =5)

Les deux premiers arguments indiquent que le canevas sera plac dans la premire ligne de la
troisime colonne. Le troisime (rowspan =3) indique quil pourra staler sur trois lignes.
Les deux derniers (padx =10, pady =5) indiquent la dimension de lespace quil faut rserver
autour de ce widget (en largeur et en hauteur).
5. Et tant que nous y sommes, profitons de cet exemple de script, que nous avons dj bien
dcortiqu, pour apprendre simplifier quelque peu notre code

Composition dinstructions pour crire un code plus compact


Python tant un langage de programmation de haut niveau, il est souvent possible (et souhaitable) de
retravailler un script afin de le rendre plus compact.
Vous pouvez par exemple assez frquemment utiliser la composition dinstructions pour appliquer la
mthode de mise en page des widgets (grid(), pack() ou place()) au moment mme o vous crez ces
widgets. Le code correspondant devient alors un peu plus simple, et parfois plus lisible. Vous pouvez
par exemple remplacer les deux lignes :
txt1 = Label(fen1, text ='Premier champ :')
txt1.grid(row =1, sticky =E)

du script prcdent par une seule, telle que :


Label(fen1, text ='Premier champ :').grid(row =1, sticky =E)

Dans cette nouvelle criture, vous pouvez constater que nous faisons lconomie de la variable
intermdiaire txt1. Nous avions utilis cette variable pour bien dgager les tapes successives de notre
dmarche, mais elle nest pas toujours indispensable. Le simple fait dinvoquer la classe Label()
provoque en effet linstanciation dun objet de cette classe, mme si lon ne mmorise pas la rfrence
de cet objet dans une variable (tkinter la conserve de toute faon dans sa reprsentation interne de la
fentre). Si lon procde ainsi, la rfrence est perdue pour le restant du script, mais elle peut tout de
mme tre transmise une mthode de mise en page telle que grid() au moment mme de linstanciation, en une seule instruction compose. Voyons cela un peu plus en dtail.
Jusqu prsent, nous avons cr des objets divers (par instanciation partir dune classe quelconque),
en les affectant chaque fois des variables. Par exemple, lorsque nous avons crit :
txt1 = Label(fen1, text ='Premier champ :')

nous avons cr une instance de la classe Label(), que nous avons assigne la variable txt1.
La variable txt1 peut alors tre utilise pour faire rfrence cette instance, partout ailleurs dans le
script, mais dans les faits nous ne lutilisons quune seule fois pour lui appliquer la mthode grid(), le
widget dont il est question ntant rien dautre quune simple tiquette descriptive. Or, crer ainsi une
nouvelle variable pour ny faire rfrence ensuite quune seule fois (et directement aprs sa cration)

Composition dinstructions pour crire un code plus compact

99

nest pas une pratique trs recommandable, puisquelle consiste rserver inutilement un certain
espace mmoire.
Lorsque ce genre de situation se prsente, il est plus judicieux dutiliser la composition dinstructions.
Par exemple, on prfrera le plus souvent remplacer les deux instructions :
somme = 45 + 72
print (somme)

par une seule instruction compose, telle que :


print (45 + 72)

on fait ainsi lconomie dune variable.


De la mme manire, lorsque lon met en place des widgets auxquels on ne souhaite plus revenir par la
suite, comme cest souvent le cas pour les widgets de la classe Label(), on peut en gnral appliquer la
mthode de mise en page (grid() , pack() ou place()) directement au moment de la cration du widget,
en une seule instruction compose.
Cela sapplique seulement aux widgets qui ne sont plus rfrencs aprs quon les ait crs. Tous les
autres doivent imprativement tre assigns des variables, afin que lon puisse encore interagir avec eux
ailleurs dans le script.
Et dans ce cas, il faut obligatoirement utiliser deux instructions distinctes, lune pour instancier le
widget, et lautre pour lui appliquer ensuite la mthode de mise en page. Vous ne pouvez pas, par
exemple, construire une instruction compose telle que :
entree = Entry(fen1).pack()

# faute de programmation !!!

En apparence, cette instruction devrait instancier un nouveau widget et lassigner la variable entree, la mise en page seffectuant dans la mme opration laide de la mthode pack().
Dans la ralit, cette instruction produit bel et bien un nouveau widget de la classe Entry(), et la
mthode pack() effectue bel et bien sa mise en page dans la fentre, mais la valeur qui est mmorise
dans la variable entree nest pas la rfrence du widget ! Cest la valeur de retour de la mthode pack() :
vous devez vous rappeler en effet que les mthodes, comme les fonctions, renvoient toujours une
valeur au programme qui les appelle. Et vous ne pouvez rien faire de cette valeur de retour : il sagit en
loccurrence dun objet vide (None).
Pour obtenir une vraie rfrence du widget, vous devez obligatoirement utiliser deux instructions :
entree = Entry(fen1)
entree.pack()

# instanciation du widget
# application de la mise en page

Lorsque vous utilisez la mthode grid(), vous pouvez simplifier encore un peu votre
code, en omettant lindication de nombreux numros de lignes et de colonnes. partir du
moment o cest la la mthode grid() qui est utilise pour positionner les widgets, tkinter
considre en effet quil existe forcment des lignes et des colonnes 50. Si un numro de
ligne ou de colonne nest pas indiqu, le widget correspondant est plac dans la premire
case vide disponible.
50 Surtout, nutilisez pas

plusieurs mthodes de positionnement diffrentes dans la mme fentre !


Les mthodes grid(), pack() et place() sont mutuellement exclusives.

100

Utilisation de fentres et de graphismes

Le script ci-dessous intgre les simplifications que nous venons dexpliquer :


from tkinter import *
fen1 = Tk()
# cration de widgets Label(), Entry(), et Checkbutton() :
Label(fen1, text = 'Premier champ :').grid(sticky =E)
Label(fen1, text = 'Deuxime :').grid(sticky =E)
Label(fen1, text = 'Troisime :').grid(sticky =E)
entr1 = Entry(fen1)
entr2 = Entry(fen1)
# ces widgets devront certainement
entr3 = Entry(fen1)
# tre rfrencs plus loin :
entr1.grid(row =0, column =1)
# il faut donc les assigner chacun
entr2.grid(row =1, column =1)
# une variable distincte
entr3.grid(row =2, column =1)
chek1 = Checkbutton(fen1, text ='Case cocher, pour voir')
chek1.grid(columnspan =2)
# cration d'un widget 'Canvas' contenant une image bitmap :
can1 = Canvas(fen1, width =160, height =160, bg ='white')
photo = PhotoImage(file ='Martin_P.gif')
can1.create_image(80,80, image =photo)
can1.grid(row =0, column =2, rowspan =4, padx =10, pady =5)
# dmarrage :
fen1.mainloop()

Modification des proprits dun objet Animation


ce stade de votre apprentissage, vous souhaitez probablement pouvoir faire apparatre un petit
dessin quelconque dans un canevas, et puis le dplacer volont, par exemple laide de boutons.
Veuillez donc crire, tester, puis analyser le script ci-dessous :
from tkinter import *
# procdure gnrale de dplacement :
def avance(gd, hb):
global x1, y1
x1, y1 = x1 +gd, y1 +hb
can1.coords(oval1, x1,y1, x1+30,y1+30)
# gestionnaires d'vnements :
def depl_gauche():
avance(-10, 0)
def depl_droite():
avance(10, 0)
def depl_haut():
avance(0, -10)
def depl_bas():
avance(0, 10)
#------ Programme principal ------# les variables suivantes seront utilises de manire globale :
x1, y1 = 10, 10
# coordonnes initiales

Modification des proprits dun objet Animation

101

# Cration du widget principal ("matre") :


fen1 = Tk()
fen1.title("Exercice d'animation avec tkinter")
# cration des widgets "esclaves" :
can1 = Canvas(fen1,bg='dark grey',height=300,width=300)
oval1 = can1.create_oval(x1,y1,x1+30,y1+30,width=2,fill='red')
can1.pack(side=LEFT)
Button(fen1,text='Quitter',command=fen1.quit).pack(side=BOTTOM)
Button(fen1,text='Gauche',command=depl_gauche).pack()
Button(fen1,text='Droite',command=depl_droite).pack()
Button(fen1,text='Haut',command=depl_haut).pack()
Button(fen1,text='Bas',command=depl_bas).pack()
# dmarrage du rceptionnaire dvnements (boucle principale) :
fen1.mainloop()

Le corps de ce programme reprend de nombreux lments connus : nous y crons une fentre fen1,
dans laquelle nous installons un canevas contenant lui-mme un cercle color, plus cinq boutons de
contrle. Veuillez remarquer au passage que nous ninstancions pas les widgets boutons dans des
variables (cest inutile, puisque nous ny faisons plus rfrence par la suite) : nous devons donc
appliquer la mthode pack() directement au moment de la cration de ces objets.
La vraie nouveaut de ce programme rside dans la fonction avance() dfinie au dbut du script.
Chaque fois quelle sera appele, cette fonction redfinira les coordonnes de lobjet cercle color
que nous avons install dans le canevas, ce qui provoquera lanimation de cet objet.
Cette manire de procder est tout fait caractristique de la programmation oriente objet : on
commence par crer des objets, puis on agit sur ces objets en modifiant leurs proprits, par lintermdiaire
de mthodes.
En programmation imprative lancienne (cest--dire sans utilisation dobjets), on anime des
figures en les effaant un endroit pour les redessiner ensuite un petit peu plus loin. En
programmation oriente objet , par contre, ces tches sont prises en charge automatiquement par
les classes dont les objets drivent, et il ne faut donc pas perdre son temps les reprogrammer.
Exercices
8.12 crivez un programme qui fait apparatre une fentre avec un canevas. Dans ce canevas on
verra deux cercles (de tailles et de couleurs diffrentes), qui sont censs reprsenter deux
astres. Des boutons doivent permettre de les dplacer volont tous les deux dans toutes les
directions. Sous le canevas, le programme doit afficher en permanence : a) la distance sparant
les deux astres; b) la force gravitationnelle quils exercent lun sur lautre (penser afficher en
haut de fentre les masses choisies pour chacun deux, ainsi que lchelle des distances). Dans
cet exercice, vous utiliserez videmment la loi de la gravitation universelle de Newton (cf.
exercice 6.16, page 58, et un manuel de Physique gnrale).

8.13 En vous inspirant du programme qui dtecte les clics de souris dans un canevas, modifiez le
programme ci-dessus pour y rduire le nombre de boutons : pour dplacer un astre, il suffira
de le choisir avec un bouton, et ensuite de cliquer sur le canevas pour que cet astre se
positionne lendroit o lon a cliqu.

102

Utilisation de fentres et de graphismes

8.14 Extension du programme ci-dessus. Faire apparatre un troisime astre, et afficher en


permanence la force rsultante agissant sur chacun des trois (en effet : chacun subit en
permanence lattraction gravitationnelle exerce par les deux autres !).
8.15 Mme exercice avec des charges lectriques (loi de Coulomb). Donner cette fois une possibilit
de choisir le signe des charges.
8.16 crivez un petit programme qui fait apparatre une fentre avec deux champs : lun indique
une temprature en degrs Celsius, et lautre la mme temprature exprime en degrs Fahrenheit. Chaque fois que lon change une quelconque des deux tempratures, lautre est corrige
en consquence. Pour convertir les degrs Fahrenheit en Celsius et vice-versa, on utilise la
formule T F =T C 1,8032 . Revoyez aussi le petit programme concernant la calculatrice
simplifie (page 90).
8.17 crivez un programme qui fait apparatre une fentre avec un canevas. Dans ce canevas,
placez un petit cercle cens reprsenter une balle. Sous le canevas, placez un bouton. Chaque
fois que lon clique sur le bouton, la balle doit avancer dune petite distance vers la droite,
jusqu ce quelle atteigne lextrmit du canevas. Si lon continue cliquer, la balle doit alors
revenir en arrire jusqu lautre extrmit, et ainsi de suite.
8.18 Amliorez le programme ci-dessus pour que la balle dcrive cette fois une trajectoire circulaire
ou elliptique dans le canevas (lorsque lon clique continuellement). Note : pour arriver au
rsultat escompt, vous devrez ncessairement dfinir une variable qui reprsentera langle
dcrit, et utiliser les fonctions sinus et cosinus pour positionner la balle en fonction de cet
angle.
8.19 Modifiez le programme ci-dessus de telle manire que la balle, en se dplaant, laisse derrire
elle une trace de la trajectoire dcrite.
8.20 Modifiez le programme ci-dessus de manire tracer dautres figures. Consultez votre
professeur pour des suggestions (courbes de Lissajous).
8.21 crivez un programme qui fait apparatre une
fentre avec un canevas et un bouton. Dans le
canevas, tracez un rectangle gris fonc, lequel
reprsentera une route, et par-dessus, une srie de
rectangles jaunes censs reprsenter un passage
pour pitons. Ajoutez quatre cercles colors pour
figurer les feux de circulation concernant les
pitons et les vhicules. Chaque utilisation du
bouton devra provoquer le changement de couleur
des feux :

8.22 crivez un programme qui montre un canevas dans lequel est dessin un circuit lectrique
simple (gnrateur + interrupteur + rsistance). La fentre doit tre pourvue de champs dentre qui permettront de paramtrer chaque lment (cest--dire choisir les valeurs des
rsistances et tensions). Linterrupteur doit tre fonctionnel (prvoyez un bouton

Modification des proprits dun objet Animation

103

<Marche/arrt> pour cela). Des tiquettes doivent afficher en permanence les tensions et
intensits rsultant des choix effectus par lutilisateur.

Animation automatique Rcursivit


Pour conclure cette premire prise de contact avec linterface graphique tkinter, voici un dernier
exemple danimation, qui fonctionne cette fois de manire autonome ds quon la mise en marche.
from tkinter import *
# dfinition des gestionnaires
# d'vnements :
def move():
"dplacement de la balle"
global x1, y1, dx, dy, flag
x1, y1 = x1 +dx, y1 + dy
if x1 >210:
x1, dx, dy = 210, 0, 15
if y1 >210:
y1, dx, dy = 210, -15, 0
if x1 <10:
x1, dx, dy = 10, 0, -15
if y1 <10:
y1, dx, dy = 10, 15, 0
can1.coords(oval1,x1,y1,x1+30,y1+30)
if flag >0:
fen1.after(50,move)
# => boucler, aprs 50 millisecondes
def stop_it():
"arrt de l'animation"
global flag
flag =0
def start_it():
"dmarrage de l'animation"
global flag
if flag ==0:
# pour ne lancer quune seule boucle
flag =1
move()
#========== Programme principal =============
# les variables suivantes seront utilises de manire globale :
x1, y1 = 10, 10
# coordonnes initiales
dx, dy = 15, 0
# 'pas' du dplacement
flag =0
# commutateur
# Cration du widget principal ("parent") :
fen1 = Tk()
fen1.title("Exercice d'animation avec tkinter")
# cration des widgets "enfants" :
can1 = Canvas(fen1,bg='dark grey',height=250, width=250)
can1.pack(side=LEFT, padx =5, pady =5)
oval1 = can1.create_oval(x1, y1, x1+30, y1+30, width=2, fill='red')
bou1 = Button(fen1,text='Quitter', width =8, command=fen1.quit)
bou1.pack(side=BOTTOM)
bou2 = Button(fen1, text='Dmarrer', width =8, command=start_it)
bou2.pack()
bou3 = Button(fen1, text='Arrter', width =8, command=stop_it)

104

Utilisation de fentres et de graphismes

bou3.pack()
# dmarrage du rceptionnaire d'vnements (boucle principale) :
fen1.mainloop()

La seule nouveaut mise en uvre dans ce script se trouve tout la fin de la dfinition de la fonction
move() : vous y noterez lutilisation de la mthode after(). Cette mthode peut sappliquer un widget
quelconque. Elle dclenche lappel dune fonction aprs quun certain laps de temps se soit coul. Ainsi par
exemple, window.after(200,qqc) dclenche pour le widget window un appel de la fonction qqc()
aprs une pause de 200 millisecondes.
Dans notre script, la fonction qui est appele par la mthode after() est la fonction move() elle-mme.
Nous utilisons donc ici pour la premire fois une technique de programmation trs puissante, que lon
appelle rcursivit. Pour faire simple, nous dirons que la rcursivit est ce qui se passe lorsqu une
fonction sappelle elle-mme. On obtient bien videmment ainsi un bouclage, qui peut se perptuer
indfiniment si lon ne prvoit pas aussi un moyen pour linterrompre.
Voyons comment cela fonctionne dans notre exemple.
La fonction move() est invoque une premire fois lorsque lon clique sur le bouton <Dmarrer>. Elle
effectue son travail (cest--dire positionner la balle). Ensuite, par lintermdiaire de la mthode
after(), elle sinvoque elle-mme aprs une petite pause. Elle repart donc pour un second tour, puis
sinvoque elle-mme nouveau, et ainsi de suite indfiniment...
Cest du moins ce qui se passerait si nous navions pas pris la prcaution de placer quelque part dans la
boucle une instruction de sortie. En loccurrence, il sagit dun simple test conditionnel : chaque
itration de la boucle, nous examinons le contenu de la variable flag laide dune instruction if. Si le
contenu de la variable flag est zro, alors le bouclage ne seffectue plus et lanimation sarrte.
Remarquez que nous avons pris la prcaution de dfinir flag comme une variable globale. Ainsi nous
pouvons aisment changer sa valeur laide dautres fonctions, en loccurrence celles que nous avons
associes aux boutons <Dmarrer> et <Arrter>.
Nous obtenons ainsi un mcanisme simple pour lancer ou arrter notre animation : un premier clic sur
le bouton <Dmarrer> assigne une valeur non-nulle la variable flag, puis provoque immdiatement
un premier appel de la fonction move(). Celle-ci sexcute, puis continue ensuite sappeler
elle-mme toutes les 50 millisecondes, tant que flag ne revient pas zro. Si lon continue cliquer sur
le bouton <Dmarrer>, la fonction move() ne peut plus tre appele, parce que la valeur de flag vaut
dsormais 1. On vite ainsi le dmarrage de plusieurs boucles concurrentes.
Le bouton <Arrter> remet flag zro, et la boucle sinterrompt.
Exercices
8.23 Dans la fonction start_it(), supprimez linstruction if flag == 0: (et lindentation des deux
lignes suivantes). Que se passe-t-il ? (Cliquez plusieurs fois sur le bouton <Dmarrer>.)
Tchez dexprimer le plus clairement possible votre explication des faits observs.

8.24 Modifiez le programme de telle faon que la balle change de couleur chaque virage .
8.25 Modifiez le programme de telle faon que la balle effectue des mouvements obliques comme
une bille de billard qui rebondit sur les bandes ( en zig-zag ).

Animation automatique Rcursivit

105

8.26 Modifiez le programme de manire obtenir dautres mouvements. Tchez par exemple dobtenir un mouvement circulaire (comme dans les exercices de la page 102).
8.27 Modifiez ce programme, ou bien crivez-en un autre similaire, de manire simuler le
mouvement dune balle qui tombe (sous leffet de la pesanteur), et rebondit sur le sol.
Attention : il sagit cette fois de mouvements acclrs !
8.28 partir des scripts prcdents, vous pouvez prsent crire un programme de jeu
fonctionnant de la manire suivante : une balle se dplace au hasard sur un canevas, vitesse
faible. Le joueur doit essayer de cliquer sur cette balle laide de la souris. Sil y arrive, il gagne
un point, mais la balle se dplace dsormais un peu plus vite, et ainsi de suite. Arrter le jeu
aprs un certain nombre de clics et afficher le score atteint.
8.29 Variante du jeu prcdent : chaque fois que le joueur parvient lattraper , la balle devient
plus petite (elle peut galement changer de couleur).
8.30 crivez un programme dans lequel voluent plusieurs balles de couleurs diffrentes, qui
rebondissent les unes sur les autres ainsi que sur les parois.
8.31 Perfectionnez le jeu des prcdents exercices en y intgrant lalgorithme ci-dessus. Il sagit
prsent pour le joueur de cliquer seulement sur la balle rouge. Un clic erron (sur une balle
dune autre couleur) lui fait perdre des points.
8.32 crivez un programme qui simule le mouvement de deux plantes tournant autour du soleil
sur des orbites circulaires diffrentes (ou deux lectrons tournant autour dun noyau
datome...).
8.33 crivez un programme pour le jeu du serpent : un serpent (constitu en fait dune courte
ligne de carrs) se dplace sur le canevas dans lune des 4 directions : droite, gauche, haut, bas.
Le joueur peut tout moment changer la direction suivie par le serpent laide des touches
flches du clavier. Sur le canevas se trouvent galement des proies (des petits cercles fixes
disposs au hasard). Il faut diriger le serpent de manire ce quil mange les proies sans
arriver en contact avec les bords du canevas. chaque fois quune proie est mange, le serpent
sallonge dun carr, le joueur gagne un point, et une nouvelle proie apparat ailleurs. La partie
sarrte lorsque le serpent touche lune des parois, ou lorsquil a atteint une certaine taille.
8.34 Perfectionnement du jeu prcdent : la partie sarrte galement si le serpent se recoupe .

9
Manipuler des fichiers

Jusqu prsent, les programmes que nous avons raliss ne traitaient quun trs petit nombre de
donnes. Nous pouvions donc chaque fois inclure ces donnes dans le corps du programme
lui-mme (par exemple dans une liste). Cette faon de procder devient cependant tout fait
inadquate lorsque lon souhaite traiter une quantit dinformations plus importante.

Utilit des fichiers


Imaginons par exemple que nous voulions crire un petit programme exerciseur qui fasse apparatre
lcran des questions choix multiple, avec traitement automatique des rponses de lutilisateur.
Comment allons-nous mmoriser le texte des questions elles-mmes ?
Lide la plus simple consiste placer chacun de ces textes dans une variable, en dbut de programme,
avec des instructions daffectation du genre :
a = "Quelle est la capitale du Guatemala ?"
b = "Qui succd Henri IV ?"
c = "Combien font 26 x 43 ?"
... etc.

Cette ide est malheureusement beaucoup trop simpliste. Tout va se compliquer en effet lorsque nous
essayerons dlaborer la suite du programme, cest--dire les instructions qui devront servir
slectionner au hasard lune ou lautre de ces questions pour les prsenter lutilisateur. Employer par
exemple une longue suite dinstructions if ... elif ... elif ... comme dans lexemple ci-dessous nest
certainement pas la bonne solution (ce serait dailleurs bien pnible crire : noubliez pas que nous
souhaitons traiter un grand nombre de questions !) :
if choix == 1:
selection = a
elif choix == 2:
selection = b
elif choix == 3:
selection = c
... etc.

La situation se prsente dj beaucoup mieux si nous faisons appel une liste :

108

Manipuler des fichiers

liste = ["Qui a vaincu Napolon Waterloo ?",


"Comment traduit-on informatique en anglais ?",
"Quelle est la formule chimique du mthane ?", ... etc ...]

On peut en effet extraire nimporte quel lment de cette liste laide de son indice. Exemple :
print(liste[2])

===>

"Quelle est la formule chimique du mthane ?"

Rappel
Lindiage commence partir de zro.

Mme si cette faon de procder est dj nettement meilleure que la prcdente, nous sommes
toujours confronts plusieurs problmes gnants :

La lisibilit du programme va se dtriorer trs vite lorsque le nombre de questions deviendra


important. En corollaire, nous accrotrons la probabilit dinsrer une erreur de syntaxe dans la
dfinition de cette longue liste. De telles erreurs seront bien difficiles dbusquer.
Lajout de nouvelles questions, ou la modification de certaines dentre elles, imposeront chaque
fois de rouvrir le code source du programme. En corollaire, il deviendra malais de retravailler ce
mme code source, puisquil comportera de nombreuses lignes de donnes encombrantes.
Lchange de donnes avec dautres programmes (peut-tre crits dans dautres langages) est tout
simplement impossible, puisque ces donnes font partie du programme lui-mme.
Cette dernire remarque nous suggre la direction prendre : il est temps que nous apprenions sparer les donnes et les programmes qui les traitent dans des fichiers diffrents.
Pour que cela devienne possible, nous devrons doter nos programmes de divers mcanismes
permettant de crer des fichiers, dy envoyer des donnes et de les rcuprer par la suite.
Les langages de programmation proposent des jeux dinstructions plus ou moins sophistiqus pour
effectuer ces tches. Lorsque les quantits de donnes deviennent trs importantes, il devient
dailleurs rapidement ncessaire de structurer les relations entre ces donnes, et lon doit alors
laborer des systmes appels bases de donnes relationnelles, dont la gestion peut savrer trs
complexe. Lorsque lon est confront ce genre de problme, il est dusage de dlguer une bonne
part du travail des logiciels trs spcialiss tels que Oracle, IBM DB2, Sybase, Adabas, PostgreSQL, MySQL,
etc. Python est parfaitement capable de dialoguer avec ces systmes, mais nous laisserons cela pour un
peu plus tard (voir : gestion dune base de donnes, page 279).
Nos ambitions prsentes sont plus modestes. Nos donnes ne se comptent pas encore par centaines de
milliers, aussi nous pouvons nous contenter de mcanismes simples pour les enregistrer dans un
fichier de taille moyenne, et les en extraire ensuite.

Travailler avec des fichiers


Lutilisation dun fichier ressemble beaucoup lutilisation dun livre. Pour utiliser un livre, vous
devez dabord le trouver ( laide de son titre), puis louvrir. Lorsque vous avez fini de lutiliser, vous le
refermez. Tant quil est ouvert, vous pouvez y lire des informations diverses, et vous pouvez aussi y
crire des annotations, mais gnralement vous ne faites pas les deux la fois. Dans tous les cas, vous
pouvez vous situer lintrieur du livre, notamment en vous aidant des numros de pages. Vous lisez

Travailler avec des fichiers

109

la plupart des livres en suivant lordre normal des pages, mais vous pouvez aussi dcider de consulter
nimporte quel paragraphe dans le dsordre.
Tout ce que nous venons de dire des livres sapplique galement aux fichiers informatiques. Un fichier
se compose de donnes enregistres sur votre disque dur, sur une disquette, une clef USB ou un CD.
Vous y accdez grce son nom (lequel peut inclure aussi un nom de rpertoire). En premire
approximation, vous pouvez considrer le contenu dun fichier comme une suite de caractres, ce qui
signifie que vous pouvez traiter ce contenu, ou une partie quelconque de celui-ci, laide des fonctions
servant traiter les chanes de caractres51.

Noms de fichiers le rpertoire courant


Pour simplifier les explications qui vont suivre, nous indiquerons seulement des noms simples pour les
fichiers que nous allons manipuler. Si vous procdez ainsi dans vos exercices, les fichiers en question
seront crs et/ou recherchs par Python dans le rpertoire courant. Celui-ci est habituellement le
rpertoire o se trouve le script lui-mme, sauf si vous lancez ce script depuis la fentre dun shell
IDLE, auquel cas le rpertoire courant est dfini au lancement de IDLE lui-mme (sous Windows, la
dfinition de ce rpertoire fait partie des proprits de licne de lancement).
Si vous travaillez avec IDLE, vous souhaiterez donc certainement forcer Python changer son
rpertoire courant, afin que celui-ci corresponde vos attentes. Pour ce faire, utilisez les commandes
suivantes en dbut de session. Nous supposons pour la dmonstration que le rpertoire vis est le
rpertoire /home/jules/exercices . Mme si vous travaillez sous Windows (o ce nest pas la rgle),
vous pouvez utiliser cette syntaxe (cest--dire des caractres / et non \ en guise de sparateurs : cest
la convention en vigueur dans le monde Unix). Python effectuera automatiquement les conversions
ncessaires, suivant que vous travaillez sous Mac OS, Linux, ou Windows52.
>>> from os import chdir
>>> chdir("/home/jules/exercices")

La premire commande importe la fonction chdir() du module os. Le module os contient toute une
srie de fonctions permettant de dialoguer avec le systme dexploitation (os = operating system), quel
que soit celui-ci.
La seconde commande provoque le changement de rpertoire (chdir =change directory).
Vous avez galement la possibilit dinsrer ces commandes en dbut de script, ou
encore dindiquer le chemin daccs complet dans le nom des fichiers que vous
manipulez, mais cela risque peut-tre dalourdir lcriture de vos programmes.
Choisissez de prfrence des noms de fichiers courts. vitez dans toute la mesure du
possible les caractres accentus, les espaces et les signes typographiques spciaux.
Dans les environnements de travail de type Unix (Mac OS, Linux, BSD...), il est souvent
recommand de nutiliser que des caractres minuscules.

51 En

toute rigueur, vous devez considrer que le contenu dun fichier est une suite doctets. La plupart des
octets peuvent effectivement tre reprsents par des caractres, mais linverse nest pas vrai : nous devrons
donc par la suite oprer une distinction nette entre les chanes doctets et les chanes de caractres.
52 Dans le cas de Windows, vous pouvez galement inclure dans ce chemin la lettre qui dsigne le priphrique
de stockage o se trouve le fichier. Par exemple : D:/home/jules/exercices.

110

Manipuler des fichiers

Les deux formes dimportation


Les lignes dinstructions que nous venons dutiliser sont loccasion dexpliquer un mcanisme
intressant. Vous savez quen complment des fonctions intgres dans le module de base, Python met
votre disposition une trs grande quantit de fonctions plus spcialises, qui sont regroupes dans
des modules. Ainsi vous connaissez dj fort bien le module math et le module tkinter.
Pour utiliser les fonctions dun module, il suffit de les importer. Mais cela peut se faire de deux
manires diffrentes, comme nous allons le voir ci-dessous. Chacune des deux mthodes prsente des
avantages et des inconvnients.
Voici un exemple de la premire mthode :
>>> import os
>>> rep_cour = os.getcwd()
>>> print rep_cour
C:\Python22\essais

La premire ligne de cet exemple importe lintgralit du module os, lequel contient de nombreuses
fonctions intressantes pour laccs au systme dexploitation. La seconde ligne utilise la fonction
getcwd() du module os53. Comme vous pouvez le constater, la fonction getcwd() renvoie le nom du
rpertoire courant (getcwd = get current working directory). Par comparaison, voici un exemple similaire
utilisant la seconde mthode dimportation :
>>> from os import getcwd
>>> rep_cour = getcwd()
>>> print(rep_cour)
C:\Python31\essais

Dans ce nouvel exemple, nous navons import du module os que la fonction getcwd(). Importe de
cette manire, la fonction sintgre notre propre code comme si nous lavions crite nous-mmes.
Dans les lignes o nous lutilisons, il nest pas ncessaire de rappeler quelle fait partie du module os.
Nous pouvons de la mme manire importer plusieurs fonctions du mme module :
>>> from math import sqrt, pi, sin, cos
>>> print(pi)
3.14159265359
>>> print(sqrt(5))
# racine carre de 5
2.2360679775
>>> print(sin(pi/6))
# sinus dun angle de 30
0.5

Nous pouvons mme importer toutes les fonctions dun module, comme dans :
from tkinter import *

Cette mthode dimportation prsente lavantage dallger lcriture du code. Elle prsente linconvnient (surtout dans sa dernire forme, celle qui importe toutes les fonctions dun module) dencom53 Le

point sparateur exprime donc ici une relation d appartenance. Il sagit dun exemple de la qualification
des noms qui sera de plus en plus largement exploite dans la suite de ce cours. Relier ainsi des noms l aide
de points est une manire de dsigner sans ambigut des lments faisant partie d ensembles, lesquels
peuvent eux-mmes faire partie d ensembles plus vastes, etc. Par exemple, l tiquette systeme.machin.truc
dsigne llment truc, qui fait partie de lensemble machin, lequel fait lui-mme partie de l ensemble
systeme. Nous verrons de nombreux exemples de cette technique de dsignation, notamment lors de notre
tude des classes dobjets.

Les deux formes dimportation

111

brer lespace de noms courant. Il se pourrait alors que certaines fonctions importes aient le mme
nom que celui dune variable dfinie par vous-mme, ou encore le mme nom quune fonction
importe depuis un autre module. Si cela se produit, lun des deux noms en conflit nest videmment
plus accessible.
Dans les programmes dune certaine importance, qui font appel un grand nombre de modules dorigines diverses, il sera donc toujours prfrable de privilgier la premire mthode, cest--dire celle
qui utilise des noms pleinement qualifis.
On fait gnralement exception cette rgle dans le cas particulier du module tkinter, parce que les
fonctions quil contient sont trs sollicites (ds lors que lon dcide dutiliser ce module).

criture squentielle dans un fichier


Sous Python, laccs aux fichiers est assur par lintermdiaire dun objet-interface particulier, que
lon appelle objet-fichier. On cre cet objet laide de la fonction intgre open()54. Celle-ci renvoie un
objet dot de mthodes spcifiques, qui vous permettront de lire et crire dans le fichier.
Lexemple ci-aprs vous montre comment ouvrir un fichier en criture, y enregistrer deux chanes de
caractres, puis le refermer. Notez bien que si le fichier nexiste pas encore, il sera cr
automatiquement. Par contre, si le nom utilis concerne un fichier prexistant qui contient dj des
donnes, les caractres que vous y enregistrerez viendront sajouter la suite de ceux qui sy trouvent
dj. Vous pouvez faire tout cet exercice directement la ligne de commande :
>>>
>>>
>>>
>>>
>>>

obFichier = open('Monfichier','a')
obFichier.write('Bonjour, fichier !')
obFichier.write("Quel beau temps, aujourd'hui !")
obFichier.close()

Notes
La premire ligne cre lobjet-fichier obFichier, lequel fait rfrence un fichier vritable (sur
disque ou disquette) dont le nom sera Monfichier. Attention : ne confondez pas le nom de fichier avec
le nom de lobjet-fichier qui y donne accs ! la suite de cet exercice, vous pouvez vrifier quil sest
bien cr sur votre systme (dans le rpertoire courant) un fichier dont le nom est Monfichier (et
dont vous pouvez visualiser le contenu laide dun diteur quelconque).
La fonction open() attend deux arguments55, qui doivent tous deux tre des chanes de caractres.
Le premier argument est le nom du fichier ouvrir, et le second est le mode douverture. 'a'
indique quil faut ouvrir ce fichier en mode ajout (append), ce qui signifie que les donnes
enregistrer doivent tre ajoutes la fin du fichier, la suite de celles qui sy trouvent
ventuellement dj. Nous aurions pu utiliser aussi le mode 'w' (pour write), mais lorsquon utilise
ce mode, Python cre toujours un nouveau fichier (vide), et lcriture des donnes commence
partir du dbut de ce nouveau fichier. Sil existe dj un fichier de mme nom, celui-ci est effac
au pralable.

54 Une telle fonction, dont la valeur de retour est un objet particulier, est souvent appele fonction-fabrique.
55 Un troisime argument peut tre ajout pour indiquer lencodage utilis (voir page 133).

112

Manipuler des fichiers

La mthode write() ralise lcriture proprement dite. Les donnes crire doivent tre fournies
en argument. Ces donnes sont enregistres dans le fichier les unes la suite des autres (cest la
raison pour laquelle on parle de fichier accs squentiel). Chaque nouvel appel de write()
continue lcriture la suite de ce qui est dj enregistr.
La mthode close() referme le fichier. Celui-ci est dsormais disponible pour tout usage.

Lecture squentielle dun fichier


Vous allez maintenant rouvrir le fichier, mais cette fois en lecture, de manire pouvoir y relire les
informations que vous avez enregistres dans ltape prcdente :
>>> ofi = open('Monfichier', 'r')
>>> t = ofi.read()
>>> print(t)
Bonjour, fichier !Quel beau temps, aujourd'hui !
>>> ofi.close()

Comme on pouvait sy attendre, la mthode read() lit les donnes prsentes dans le fichier et les
transfre dans une variable de type chane de caractres (string) . Si on utilise cette mthode sans
argument, la totalit du fichier est transfre.

Notes
Le fichier que nous voulons lire sappelle Monfichier. Linstruction douverture de fichier devra
donc ncessairement faire rfrence ce nom l. Si le fichier nexiste pas, nous obtenons un
message derreur. Exemple :
>>> ofi = open('Monficier','r')
IOError: [Errno 2] No such file or directory: 'Monficier'

Par contre, nous ne sommes tenus aucune obligation concernant le nom choisir pour lobjet-fichier. Cest un nom de variable quelconque. Ainsi donc, dans notre premire instruction, nous
avons choisi de crer un objet-fichier ofi, faisant rfrence au fichier rel Monfichier, lequel est
ouvert en lecture (argument r).
Les deux chanes de caractres que nous avions entres dans le fichier sont prsent accoles en
une seule. Cest normal, puisque nous navons fourni aucun caractre de sparation lorsque nous
les avons enregistres. Nous verrons un peu plus loin comment enregistrer des lignes de texte
distinctes.
La mthode read() peut galement tre utilise avec un argument. Celui-ci indiquera combien de
caractres doivent tre lus, partir de la position dj atteinte dans le fichier :
>>> ofi = open('Monfichier', 'r')
>>> t = ofi.read(7)
>>> print(t)
Bonjour
>>> t = ofi.read(15)
>>> print(t)
, fichier !Quel

Sil ne reste pas assez de caractres au fichier pour satisfaire la demande, la lecture sarrte tout
simplement la fin du fichier :

Lecture squentielle dun fichier

113

>>> t = ofi.read(1000)
>>> print(t)
beau temps, aujourd'hui !

Si la fin du fichier est dj atteinte, read() renvoie une chane vide :


>>> t = ofi.read()
>>> print(t)

Noubliez pas de refermer le fichier aprs usage :


>>> ofi.close()
Dans tout ce qui prcde, nous avons admis sans explication que les chanes de
caractres taient changes telles quelles entre linterprteur Python et le fichier. En
ralit, ceci est inexact, parce que les squences de caractres doivent tre converties
en squences doctets pour pouvoir tre mmorises dans les fichiers. De plus, il existe
malheureusement diffrentes normes pour cela. En toute rigueur, il faudrait donc prciser
Python la norme dencodage que vous souhaitez utiliser dans vos fichiers : nous
verrons comment faire au chapitre suivant. En attendant, vous pouvez tabler sur le fait
que Python utilise par dfaut la norme en vigueur sur votre systme dexploitation, ce qui
devrait vous viter tout problme pour ces premiers exercices. Si toutefois vos caractres
accentus saffichent bizarrement, veuillez lignorer provisoirement.

Linstruction break pour sortir dune boucle


Il va de soi que les boucles de programmation simposent lorsque lon doit traiter un fichier dont on ne
connat pas ncessairement le contenu lavance. Lide de base consistera lire ce fichier morceau
par morceau, jusqu ce que lon ait atteint la fin du fichier.
La fonction ci-dessous illustre cette ide. Elle copie lintgralit dun fichier, quelle que soit sa taille, en
transfrant des portions de 50 caractres la fois :
def copieFichier(source, destination):
"copie intgrale d'un fichier"
fs = open(source, 'r')
fd = open(destination, 'w')
while 1:
txt = fs.read(50)
if txt =="":
break
fd.write(txt)
fs.close()
fd.close()
return

Si vous voulez tester cette fonction, vous devez lui fournir deux arguments : le premier est le nom du
fichier original, le second est le nom donner au fichier qui accueillera la copie. Exemple :
copieFichier('Monfichier','Tonfichier')

Vous aurez remarqu que la boucle while utilise dans cette fonction est construite dune manire
diffrente de ce que vous avez rencontr prcdemment. Vous savez en effet que linstruction while
doit toujours tre suivie dune condition valuer ; le bloc dinstructions qui suit est alors excut en
boucle, aussi longtemps que cette condition reste vraie. Or nous avons remplac ici la condition va-

114

Manipuler des fichiers

luer par une simple constante, et vous savez galement56 que linterprteur Python considre comme
vraie toute valeur numrique diffrente de zro.
Une boucle while construite comme nous lavons fait ci-dessus devrait donc boucler indfiniment,
puisque la condition de continuation reste toujours vraie. Nous pouvons cependant interrompre ce
bouclage en faisant appel linstruction break, laquelle permet ventuellement de mettre en place
plusieurs mcanismes de sortie diffrents pour une mme boucle :
while <condition 1> :
--- instructions diverses --if <condition 2> :
break
--- instructions diverses --if <condition 3>:
break
etc.

Dans notre fonction copieFichier(), il est facile de voir que linstruction break sexcutera seulement
lorsque la fin du fichier aura t atteinte.

Fichiers texte
Un fichier texte est un fichier qui contient des caractres imprimables57 et des espaces organiss en
lignes successives, ces lignes tant spares les unes des autres par un caractre spcial non
imprimable appel marqueur de fin de ligne58.
Les fichiers texte sont donc des fichiers que nous pouvons lire et comprendre laide dun simple
diteur de texte, par opposition aux fichiers binaires dont le contenu est au moins en partie
inintelligible pour un lecteur humain, et qui ne prend son sens que lorsquil est dcod par un logiciel
spcifique. Par exemple, les fichiers contenant des images, des sons, des vidos, etc. sont presque
toujours des fichiers binaires. Nous donnons un petit exemple de traitement de fichier binaire un peu
plus loin, mais dans le cadre de ce cours, nous nous intresserons presque exclusivement aux fichiers
texte.
Il est trs facile de traiter des fichiers texte avec Python. Par exemple, les instructions suivantes
suffisent pour crer un fichier texte de quatre lignes :
>>>
>>>
>>>
>>>

f = open("Fichiertexte", "w")
f.write("Ceci est la ligne un\nVoici la ligne deux\n")
f.write("Voici la ligne trois\nVoici la ligne quatre\n")
f.close()
56 Voir page 53 : Vracit/fausset d une expression
57 En toute rigueur, un tel fichier contient des octets

imprimables dont les valeurs peuvent reprsenter


des caractres typographiques dans une norme dencodage bien dtermine. Nous tudierons cela plus en
dtail au chapitre suivant. Concrtement, il sagit doctets de valeur numrique comprise entre 32 et 255. Les
octets de valeur infrieure 32 correspondent des codes de contrle des anciens tltypes et ne peuvent
gnralement pas tre reprsents par des caractres.
58 Suivant le systme dexploitation utilis, le codage correspondant au marqueur de fin de ligne peut tre
diffrent. Sous Windows, par exemple, il sagit dune squence de deux octets (retour chariot et saut de
ligne), alors que dans les systmes de type Unix (comme Linux) il s agit dun seul saut de ligne, Mac OS pour
sa part utilisant un seul retour chariot. En principe, vous n avez pas vous proccuper de ces diffrences.
Lors des oprations dcriture, Python utilise la convention en vigueur sur votre systme d exploitation. Pour
la lecture, Python interprte correctement chacune des trois conventions (qui sont donc considres comme
quivalentes).

Fichiers texte

115

Notez bien le marqueur de fin de ligne \n insr dans les chanes de caractres, aux endroits o lon
souhaite sparer les lignes de texte dans lenregistrement. Sans ce marqueur, les caractres seraient
enregistrs les uns la suite des autres, comme dans les exemples prcdents.
Lors des oprations de lecture, les lignes dun fichier texte peuvent tre extraites sparment les unes
des autres. La mthode readline(), par exemple, ne lit quune seule ligne la fois (en incluant le
caractre de fin de ligne) :
>>> f = open('Fichiertexte','r')
>>> t = f.readline()
>>> print(t)
Ceci est la ligne un
>>> print(f.readline())
Voici la ligne deux

La mthode readlines() transfre toutes les lignes restantes dans une liste de chanes :
>>> t = f.readlines()
>>> print(t)
['Voici la ligne trois\n', 'Voici la ligne quatre\n']
>>> f.close()

Remarques
La liste apparat ci-dessus en format brut, avec des apostrophes pour dlimiter les chanes, et les
caractres spciaux sous leur forme conventionnelle. Vous pourrez bien videmment parcourir
cette liste ( laide dune boucle while, par exemple) pour en extraire les chanes individuelles.
La mthode readlines() permet donc de lire lintgralit dun fichier en une instruction
seulement. Cela nest possible toutefois que si le fichier lire nest pas trop gros : puisquil est
copi intgralement dans une variable, cest--dire dans la mmoire vive de lordinateur, il faut
que la taille de celle-ci soit suffisante. Si vous devez traiter de gros fichiers, utilisez plutt la
mthode readline() dans une boucle, comme le montrera lexemple suivant.
Notez bien que readline() est une mthode qui renvoie une chane de caractres, alors que la
mthode readlines() renvoie une liste. la fin du fichier, readline() renvoie une chane vide,
tandis que readlines() renvoie une liste vide.
Le script qui suit vous montre comment crer une fonction destine effectuer un certain traitement
sur un fichier texte. En loccurrence, il sagit ici de recopier un fichier texte, en omettant toutes les
lignes qui commencent par un caractre # :
def filtre(source,destination):
"recopier un fichier en liminant les lignes de remarques"
fs = open(source, 'r')
fd = open(destination, 'w')
while 1:
txt = fs.readline()
if txt =='':
break
if txt[0] != '#':
fd.write(txt)
fs.close()
fd.close()
return

116

Manipuler des fichiers

Pour appeler cette fonction, vous devez utiliser deux arguments : le nom du fichier original, et le nom
du fichier destin recevoir la copie filtre. Exemple :
filtre('test.txt', 'test_f.txt')

Enregistrement et restitution de variables diverses


Largument de la mthode write() utilise avec un fichier texte doit tre une chane de caractres.
Avec ce que nous avons appris jusqu prsent, nous ne pouvons donc enregistrer dautres types de
valeurs quen les transformant dabord en chanes de caractres (string). Nous pouvons raliser cela
laide de la fonction intgre str() :
>>> x = 52
>>> f.write(str(x))

Nous verrons plus loin quil existe dautres possibilits pour convertir des valeurs numriques en
chanes de caractres (voir ce sujet : Formatage des chanes de caractres, page 138). Mais la question
nest pas vraiment l. Si nous enregistrons les valeurs numriques en les transformant dabord en
chanes de caractres, nous risquons de ne plus pouvoir les re-transformer correctement en valeurs
numriques lorsque nous allons relire le fichier. Exemple :
>>> a = 5
>>> b = 2.83
>>> c = 67
>>> f = open('Monfichier', 'w')
>>> f.write(str(a))
>>> f.write(str(b))
>>> f.write(str(c))
>>> f.close()
>>> f = open('Monfichier', 'r')
>>> print(f.read())
52.8367
>>> f.close()

Nous avons enregistr trois valeurs numriques. Mais comment pouvons-nous les distinguer dans la
chane de caractres rsultante, lorsque nous effectuons la lecture du fichier ? Cest impossible ! Rien
ne nous indique dailleurs quil y a l trois valeurs plutt quune seule, ou 2, ou 4
Il existe plusieurs solutions ce genre de problmes. Lune des meilleures consiste importer un
module Python spcialis : le module pickle59. Voici comment il sutilise :
>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>>

import pickle
a, b, c = 27, 12.96, [5, 4.83, "Ren"]
f =open('donnees_test', 'wb')
pickle.dump(a, f)
pickle.dump(b, f)
pickle.dump(c, f)
f.close()
f = open('donnes_test', 'rb')
j = pickle.load(f)
k = pickle.load(f)
l = pickle.load(f)
print(j, type(j))
59 En anglais, le terme

pickle signifie conserver . Le module a t nomm ainsi parce qu il sert effectivement


enregistrer des donnes en conservant leur type.

Enregistrement et restitution de variables diverses

117

27 <class 'int'>
>>> print(k, type(k))
12.96 <class 'float'>
>>> print(l, type(l))
[5, 4.83, 'Ren'] <class 'list'>
>>> f.close()

Comme vous pouvez le constater dans ce court exemple, le module pickle permet denregistrer des
donnes avec conservation de leur type. Les contenus des trois variables a, b et c sont enregistrs dans
le fichier donnees_test, et ensuite fidlement restitus, avec leur type, dans les variables j, k et l. Vous
pouvez donc mmoriser ainsi des entiers, des rels, des chanes de caractres, des listes, et dautres
types de donnes que nous tudierons plus loin.
Attention, les fichiers traits laide des fonctions du module pickle ne seront pas des fichiers texte,
mais bien des fichiers binaires60. Pour cette raison, ils doivent obligatoirement tre ouverts comme tels
laide de la fonction open(). Vous utiliserez largument 'wb' pour ouvrir un fichier binaire en criture
(comme la 3e ligne de notre exemple), et largument 'rb' pour ouvrir un fichier binaire en lecture
(comme la 8e ligne de notre exemple).
La fonction dump() du module pickle attend deux arguments : le premier est la variable enregistrer,
le second est lobjet fichier dans lequel on travaille. La fonction pickle.load() effectue le travail
inverse, cest--dire la restitution de chaque variable avec son type.

Gestion des exceptions : les instructions try except else


Les exceptions sont les oprations queffectue un interprteur ou un compilateur lorsquune erreur est
dtecte au cours de lexcution dun programme. En rgle gnrale, lexcution du programme est
alors interrompue, et un message derreur plus ou moins explicite est affich. Exemple :
>>> print(55/0)
ZeroDivisionError: int division or modulo by zero

Dautres informations complmentaires sont affiches, indiquant notamment quel


endroit du script lerreur a t dtecte, mais nous ne les reproduisons pas ici.

Le message derreur proprement dit comporte deux parties spares par un double point : dabord le
type derreur, et ensuite une information spcifique de cette erreur.
Dans de nombreux cas, il est possible de prvoir lavance certaines des erreurs qui risquent de se
produire tel ou tel endroit du programme et dinclure cet endroit des instructions particulires, qui
seront actives seulement si ces erreurs se produisent. Dans les langages de niveau lev comme
Python, il est galement possible dassocier un mcanisme de surveillance tout un ensemble dinstructions, et donc de simplifier le traitement des erreurs qui peuvent se produire dans nimporte laquelle
de ces instructions.

60 Dans

les versions de Python antrieures la version 3.0, le module pickle sutilisait avec des fichiers texte
(mais les chanes de caractres taient traites en interne avec des conventions diffrentes). Les fichiers de
donnes crs avec ces diffrentes versions de Python ne sont donc pas directement compatibles. Des
convertisseurs existent.

118

Manipuler des fichiers

Un mcanisme de ce type sappelle en gnral mcanisme de traitement des exceptions. Celui de Python
utilise lensemble dinstructions try - except else, qui permettent dintercepter une erreur et dexcuter une portion de script spcifique de cette erreur. Il fonctionne comme suit.
Le bloc dinstructions qui suit directement une instruction try est excut par Python sous rserve. Si
une erreur survient pendant lexcution de lune de ces instructions, alors Python annule cette
instruction fautive et excute sa place le code inclus dans le bloc qui suit linstruction except. Si aucune erreur ne sest produite dans les instructions qui suivent try, alors cest le bloc qui suit linstruction else qui est excut (si cette instruction est prsente). Dans tous les cas, lexcution du
programme peut se poursuivre ensuite avec les instructions ultrieures.
Considrons par exemple un script qui demande lutilisateur dentrer un nom de fichier, lequel
fichier tant destin tre ouvert en lecture. Si le fichier nexiste pas, nous ne voulons pas que le
programme se plante . Nous voulons quun avertissement soit affich, et ventuellement que lutilisateur puisse essayer dentrer un autre nom.
filename = input("Veuillez entrer un nom de fichier : ")
try:
f = open(filename, "r")
except:
print("Le fichier", filename, "est introuvable")

Si nous estimons que ce genre de test est susceptible de rendre service plusieurs endroits d un
programme, nous pouvons aussi linclure dans une fonction :
def existe(fname):
try:
f = open(fname,'r')
f.close()
return 1
except:
return 0
filename = input("Veuillez entrer le nom du fichier : ")
if existe(filename):
print("Ce fichier existe bel et bien.")
else:
print("Le fichier", filename, "est introuvable.")

Il est galement possible de faire suivre linstruction try de plusieurs blocs except, chacun dentre eux
traitant un type derreur spcifique, mais nous ne dvelopperons pas ces complments ici. Veuillez
consulter un ouvrage de rfrence sur Python si ncessaire.
Exercices
9.1 crivez un script qui permette de crer et de relire aisment un fichier texte. Votre
programme demandera dabord lutilisateur dentrer le nom du fichier. Ensuite il lui
proposera le choix, soit denregistrer de nouvelles lignes de texte, soit dafficher le contenu du
fichier.
Lutilisateur devra pouvoir entrer ses lignes de texte successives en utilisant simplement la
touche <Enter> pour les sparer les unes des autres. Pour terminer les entres, il lui suffira
dentrer une ligne vide (cest--dire utiliser la touche <Enter> seule).
Laffichage du contenu devra montrer les lignes du fichier spares les unes des autres de la
manire la plus naturelle (les codes de fin de ligne ne doivent pas apparatre).

Gestion des exceptions : les instructions try except else

119

9.2

Considrons que vous avez votre disposition un fichier texte contenant des phrases de
diffrentes longueurs. crivez un script qui recherche et affiche la phrase la plus longue.

9.3

crivez un script qui gnre automatiquement un fichier texte contenant les tables de
multiplication de 2 30 (chacune dentre elles incluant 20 termes seulement).

9.4

crivez un script qui recopie un fichier texte en triplant tous les espaces entre les mots.

9.5

Vous avez votre disposition un fichier texte dont chaque ligne est la reprsentation dune
valeur numrique de type rel (mais sans exposants). Par exemple :
14.896
7894.6
123.278
etc.

crivez un script qui recopie ces valeurs dans un autre fichier en les arrondissant en nombres
entiers (larrondi doit tre correct).
9.6

crivez un script qui compare les contenus de deux fichiers et signale la premire diffrence
rencontre.

9.7

partir de deux fichiers prexistants A et B, construisez un fichier C qui contienne


alternativement un lment de A, un lment de B, un lment de A... et ainsi de suite jusqu
atteindre la fin de lun des deux fichiers originaux. Compltez ensuite C avec les lments
restant sur lautre.

9.8

crivez un script qui permette dencoder un fichier texte dont les lignes contiendront chacune
les noms, prnom, adresse, code postal et no de tlphone de diffrentes personnes (considrez
par exemple quil sagit des membres dun club).

9.9

crivez un script qui recopie le fichier utilis dans lexercice prcdent, en y ajoutant la date
de naissance et le sexe des personnes (lordinateur devra afficher les lignes une par une et
demander lutilisateur dentrer pour chacune les donnes complmentaires).

9.10 Considrons que vous avez fait les exercices prcdents et que vous disposez prsent dun
fichier contenant les coordonnes dun certain nombre de personnes. crivez un script qui
permette dextraire de ce fichier les lignes qui correspondent un code postal bien dtermin.
9.11 Modifiez le script de lexercice prcdent, de manire retrouver les lignes correspondant
des prnoms dont la premire lettre est situe entre F et M (inclus) dans lalphabet.
9.12 crivez des fonctions qui effectuent le mme travail que celles du module pickle (voir
page 116). Ces fonctions doivent permettre lenregistrement de variables diverses dans un
fichier texte, en les accompagnant systmatiquement dinformations concernant leur format
exact.

10
Approfondir les structures de
donnes

10

Jusqu prsent, nous nous sommes contents doprations assez simples. Nous allons maintenant
passer la vitesse suprieure. Les structures de donnes que vous avez utilises jusquici prsentent
quelques caractristiques que vous ne connaissez pas encore, et il est galement temps de vous faire
dcouvrir dautres structures plus complexes.

Le point sur les chanes de caractres


Nous avons dj rencontr les chanes de caractres au chapitre 5. la diffrence des donnes
numriques, qui sont des entits singulires, les chanes de caractres constituent un type de donne
composite. Nous entendons par l une entit bien dfinie qui est faite elle-mme dun ensemble dentits plus petites, en loccurrence : les caractres. Suivant les circonstances, nous serons amens
traiter une telle donne composite, tantt comme un seul objet, tantt comme une suite ordonne dlments. Dans ce dernier cas, nous souhaiterons probablement pouvoir accder chacun de ces lments
titre individuel.
En fait, les chanes de caractres font partie dune catgorie dobjets Python que lon appelle des squences, et dont font partie aussi les listes et les tuples. On peut effectuer sur les squences tout un
ensemble doprations similaires. Vous en connaissez dj quelques unes, et nous allons en dcrire
quelques autres dans les paragraphes suivants.

Indiage, extraction, longueur


Petit rappel du chapitre 5 : les chanes sont des squences de caractres. Chacun de ceux-ci occupe une
place prcise dans la squence. Sous Python, les lments dune squence sont toujours indics (ou
numrots) de la mme manire, cest--dire partir de zro. Pour extraire un caractre dune chane, il
suffit daccoler au nom de la variable qui contient cette chane, son indice entre crochets :
>>> nom = 'Cdric'
>>> print(nom[1], nom[3], nom[5])
r c

122

Approfondir les structures de donnes

Il est souvent utile de pouvoir dsigner lemplacement dun caractre par rapport la fin de la chane.
Pour cela, il suffit dutiliser des indices ngatifs. Ainsi -1 dsignera le dernier caractre, -2 lavantdernier, etc. :
>>> print (nom[-1], nom[-2], nom[-4], nom[-6])
cid
>>>

Si lon dsire dterminer le nombre de caractres prsents dans une chane, on utilise la fonction
intgre len() :
>>> print(len(nom))
6

Extraction de fragments de chanes


Il arrive frquemment, lorsque lon travaille avec des chanes, que lon souhaite extraire une petite
chane dune chane plus longue. Python propose pour cela une technique simple que lon appelle slicing ( dcoupage en tranches ). Elle consiste indiquer entre crochets les indices correspondant au
dbut et la fin de la tranche que lon souhaite extraire :
>>> ch = "Juliette"
>>> print(ch[0:3])
Jul

Dans la tranche [n,m], le nime caractre est inclus, mais pas le mime. Si vous voulez mmoriser
aisment ce mcanisme, il faut vous reprsenter que les indices pointent des emplacements situs
entre les caractres, comme dans le schma ci-dessous :

Au vu de ce schma, il nest pas difficile de comprendre que ch[3:7] extraira iett


Les indices de dcoupage ont des valeurs par dfaut : un premier indice non dfini est considr
comme zro, tandis que le second indice omis prend par dfaut la taille de la chane complte :
>>> print(ch[:3])
Jul
>>> print(ch[3:])
iette

# les 3 premiers caractres


# tout ce qui suit les 3 premiers caractres

Les caractres accentus ne doivent pas faire problme :


>>> ch = 'Adlade'
>>> print(ch[:3], ch[4:8])
Ad ade

Le point sur les chanes de caractres

123

Concatnation, rptition
Les chanes peuvent tre concatnes avec loprateur + et rptes avec loprateur * :
>>> n = 'abc' + 'def'
# concatnation
>>> m = 'zut ! ' * 4
# rptition
>>> print(n, m)
abcdef zut ! zut ! zut ! zut !

Remarquez au passage que les oprateurs + et * peuvent aussi tre utiliss pour laddition et la
multiplication lorsquils sappliquent des arguments numriques. Le fait que les mmes oprateurs
puissent fonctionner diffremment en fonction du contexte dans lequel on les utilise est un
mcanisme fort intressant que lon appelle surcharge des oprateurs. Dans dautres langages, la
surcharge des oprateurs nest pas toujours possible : on doit alors utiliser des symboles diffrents
pour laddition et la concatnation, par exemple.
Exercices
10.1 Dterminez vous-mme ce qui se passe, dans la technique de slicing, lorsque lun ou lautre des
indices de dcoupage est erron, et dcrivez cela le mieux possible. (Si le second indice est plus
petit que le premier, par exemple, ou bien si le second indice est plus grand que la taille de la
chane).

10.2 Dcoupez une grande chane en fragments de 5 caractres chacun. Rassemblez ces morceaux
dans lordre inverse. La chane doit pouvoir contenir des caractres accentus.
10.3 Tchez dcrire une petite fonction trouve() qui fera exactement le contraire de ce que fait
loprateur dindexage (cest--dire les crochets [ ]). Au lieu de partir dun index donn pour
retrouver le caractre correspondant, cette fonction devra retrouver lindex correspondant
un caractre donn.
En dautres termes, il sagit dcrire une fonction qui attend deux arguments : le nom de la
chane traiter et le caractre trouver. La fonction doit fournir en retour lindex du premier
caractre de ce type dans la chane. Ainsi par exemple, linstruction :
print(trouve("Juliette & Romo", "&"))

devra afficher : 9
Attention : il faut penser tous les cas possibles. Il faut notamment veiller ce que la fonction
renvoie une valeur particulire (par exemple la valeur -1) si le caractre recherch nexiste pas
dans la chane traite. Les caractres accentus doivent tre accepts.
10.4 Amliorez la fonction de lexercice prcdent en lui ajoutant un troisime paramtre : lindex
partir duquel la recherche doit seffectuer dans la chane. Ainsi par exemple, linstruction :
print(trouve ("Csar & Cloptre", "r", 5))

devra afficher : 15 (et non 4 !).


10.5 crivez une fonction compteCar() qui compte le nombre doccurrences dun caractre donn
dans une chane. Ainsi :
print(compteCar("ananas au jus","a"))

devra afficher : 4
print(compteCar("Gdon est dj l",""))

devra afficher : 3.

124

Approfondir les structures de donnes

Parcours dune squence : linstruction for ... in ...


Il arrive trs souvent que lon doive traiter lintgralit dune chane caractre par caractre, du
premier jusquau dernier, pour effectuer partir de chacun deux une opration quelconque. Nous
appellerons cette opration un parcours. En nous limitant aux outils Python que nous connaissons dj,
nous pouvons envisager dencoder un tel parcours laide dune boucle, articule autour de linstruction while :
>>>
>>>
>>>
...
...
...
J *

nom ="Josphine"
index =0
while index < len(nom):
print(nom[iindex] + ' *', end =' ')
index = index +1
o * s * * p * h * i * n * e *

Cette boucle parcourt donc la chane nom pour en extraire un un tous les caractres, lesquels sont
ensuite imprims avec interposition dastrisques. Notez bien que la condition utilise avec linstruction while est index < len(nom), ce qui signifie que le bouclage doit seffectuer jusqu ce que lon
soit arriv lindice numro 9 (la chane compte en effet 10 caractres). Nous aurons effectivement
trait tous les caractres de la chane, puisque ceux-ci sont indics de 0 9.
Le parcours dune squence est une opration trs frquente en programmation. Pour en faciliter lcriture, Python vous propose une structure de boucle plus approprie que la boucle while, base sur le
couple dinstructions for ... in ... :
Avec ces instructions, le programme ci-dessus devient :
>>> nom ="Cloptre"
>>> for car in nom:
...
print(car + ' *', end =' ')
...
C * l * * o * p * * t * r * e *

Comme vous pouvez le constater, cette structure de boucle est plus compacte. Elle vous vite davoir
dfinir et incrmenter une variable spcifique (un compteur ) pour grer lindice du caractre que
vous voulez traiter chaque itration (cest Python qui sen charge). La structure for ... in ... ne
montre que lessentiel, savoir que la variable car contiendra successivement tous les caractres de la
chane, du premier jusquau dernier.
Linstruction for permet donc dcrire des boucles, dans lesquelles litration traite successivement tous
les lments dune squence donne. Dans lexemple ci-dessus, la squence tait une chane de caractres.
Lexemple ci-aprs dmontre que lon peut appliquer le mme traitement aux listes (et il en sera de
mme pour les tuples tudis plus loin) :
liste = ['chien', 'chat', 'crocodile', 'lphant']
for animal in liste:
print('longueur de la chane', animal, '=', len(animal))

Lexcution de ce script donne :


longueur
longueur
longueur
longueur

de
de
de
de

la
la
la
la

chane
chane
chane
chane

chien = 5
chat = 4
crocodile = 9
lphant = 8

Le point sur les chanes de caractres

125

Linstruction for ... in ... : est un nouvel exemple dinstruction compose. Noubliez donc pas le double
point obligatoire la fin de la ligne, et lindentation pour le bloc dinstructions qui suit.
Le nom qui suit le mot rserv in est celui de la squence quil faut traiter. Le nom qui suit le mot rserv
for est celui que vous choisissez pour la variable destine contenir successivement tous les lments
de la squence. Cette variable est dfinie automatiquement (cest--dire quil est inutile de la dfinir au
pralable), et son type est automatiquement adapt celui de llment de la squence qui est en cours de
traitement (rappelons en effet que dans le cas dune liste, tous les lments ne sont pas
ncessairement du mme type). Exemple :
divers = ['lzard', 3, 17.25, [5, 'Jean']]
for e in divers:
print(e, type(e))

Lexcution de ce script donne :


lzard <class 'str'>
3 <class 'int'>
17.25 <class 'float'>
[5, 'Jean'] <class 'list'>

Bien que les lments de la liste divers soient tous de types diffrents (une chane de caractres, un
entier, un rel, une liste), on peut affecter successivement leurs contenus la variable e, sans que des
erreurs sensuivent (ceci est rendu possible grce au typage dynamique des variables Python).
Exercices
10.6 Dans un conte amricain, huit petits canetons sappellent respectivement : Jack, Kack, Lack,
Mack, Nack, Oack, Pack et Qack. crivez un petit script qui gnre tous ces noms partir des deux
chanes suivantes :
prefixes = 'JKLMNOP' et suffixe = 'ack'

Si vous utilisez une instruction for ... in ..., votre script ne devrait comporter que deux lignes.
10.7 Dans un script, crivez une fonction qui recherche le nombre de mots contenus dans une
phrase donne.
10.8 crivez un script qui recherche le nombre de caractres e, , , , contenus dans une phrase
donne.

Appartenance dun lment une squence : linstruction in utilise seule


Linstruction in peut tre utilise indpendamment de for, pour vrifier si un lment donn fait partie ou
non dune squence. Vous pouvez par exemple vous servir de in pour vrifier si tel caractre
alphabtique fait partie dun groupe bien dtermin :
car = "e"
voyelles = "aeiouyAEIOUY"
if car in voyelles:
print(car, "est une voyelle")

126

Approfondir les structures de donnes

Dune manire similaire, vous pouvez vrifier lappartenance dun lment une liste :
n = 5
premiers = [1, 2, 3, 5, 7, 11, 13, 17]
if n in premiers:
print(n, "fait partie de notre liste de nombres premiers")

Cette instruction trs puissante effectue donc elle seule un vritable parcours de la
squence. titre dexercice, crivez les instructions qui effectueraient le mme travail
laide dune boucle classique utilisant linstruction while.

Exercices
10.9 crivez une fonction estUnChiffre() qui renvoie vrai, si largument transmis est un chiffre, et
faux sinon. Testez ainsi tous les caractres dune chane en la parcourant laide dune boucle
for.

10.10 crivez une fonction estUneMaj() qui renvoie vrai si largument transmis est une majuscule.
Tchez de tenir compte des majuscules accentues !
10.11 crivez une fonction chaineListe() qui convertisse une phrase en une liste de mots.
10.12 Utilisez les fonctions dfinies dans les exercices prcdents pour crire un script qui puisse
extraire dun texte tous les mots qui commencent par une majuscule.
10.13 Utilisez les fonctions dfinies dans les exercices prcdents pour crire une fonction qui
renvoie le nombre de caractres majuscules contenus dans une phrase donne en argument.

Les chanes sont des squences non modifiables


Vous ne pouvez pas modifier le contenu dune chane existante. En dautres termes, vous ne pouvez
pas utiliser loprateur [ ] dans la partie gauche dune instruction daffectation. Essayez par exemple
dexcuter le petit script suivant (qui cherche intuitivement remplacer une lettre dans une chane) :
salut = 'bonjour tous'
salut[0] = 'B'
print(salut)

Le rsultat attendu par le programmeur qui a crit ces instructions est Bonjour tous (avec un B
majuscule). Mais contrairement ses attentes, ce script lve une erreur du genre : TypeError: 'str' object
does not support item assignment. Cette erreur est provoque la deuxime ligne du script. On y essaie de
remplacer une lettre par une autre dans la chane, mais cela nest pas permis.
Par contre, le script ci-dessous fonctionne parfaitement :
salut = 'bonjour tous'
salut = 'B' + salut[1:]
print salut

Dans cet autre exemple, en effet, nous ne modifions pas la chane salut. Nous en recrons une
nouvelle, avec le mme nom, la deuxime ligne du script ( partir dun morceau de la prcdente,
soit, mais quimporte : il sagit bien dune nouvelle chane).

Le point sur les chanes de caractres

127

Les chanes sont comparables


Tous les oprateurs de comparaison dont nous avons parl propos des instructions de contrle de
flux (cest--dire les instructions if ... elif ... else) fonctionnent aussi avec les chanes de caractres.
Cela peut vous tre utile pour trier des mots par ordre alphabtique :
while True:
mot = input("Entrez un mot quelconque : (<enter> pour terminer)")
if mot =="":
break
if mot < "limonade":
place = "prcde"
elif mot > "limonade":
place = "suit"
else:
place = "se confond avec"
print("Le mot", mot, place, "le mot 'limonade' dans l'ordre alphabtique")

Ces comparaisons sont possibles, parce que dans toutes les normes dencodage, les codes numriques
reprsentant les caractres ont t attribus dans lordre alphabtique, tout au moins pour les
caractres non accentus. Dans le systme de codage ASCII, par exemple, A=65, B=66, C=67, etc.
Comprenez cependant que cela ne fonctionne bien que pour des mots qui sont tous entirement en
minuscules, ou entirement en majuscules, et qui ne comportent aucun caractre accentu. Vous
savez en effet que les majuscules et minuscules utilisent des ensembles de codes distincts. Quant aux
caractres accentus, vous avez vu quils sont encods en dehors de lensemble constitu par les
caractres du standard ASCII. Construire un algorithme de tri alphabtique qui prenne en compte la
fois la casse des caractres et tous leurs accents nest donc pas une mince affaire !

La norme Unicode
ce stade, il est utile de sintresser aux valeurs des identifiants numriques associs chaque
caractre. Sous Python 3, les chanes de caractres (donnes de type string) sont dsormais des chanes
Unicode61, ce qui signifie que les identifiants numriques de leurs caractres sont uniques (il ne peut
exister quun seul caractre typographique pour chaque code) et universels (les identifiants choisis
couvrent la gamme complte de tous les caractres utiliss dans les diffrentes langues du monde
entier).
lorigine du dveloppement des technologies informatiques, alors que les capacits de mmorisation
des ordinateurs taient encore assez limites, on nimaginait pas que ceux-ci seraient utiliss un jour
pour traiter dautres textes que des communications techniques, essentiellement en anglais. Il
semblait donc tout--fait raisonnable de ne prvoir pour celles-ci quun jeu de caractres restreint, de
manire pouvoir reprsenter chacun de ces caractres avec un petit nombre de bits, et ainsi occuper
aussi peu despace que possible dans les coteuses units de stockage de lpoque. Le jeu de caractres
ASCII62 fut donc choisi en ce temps l, avec lestimation que 128 caractres suffiraient ( savoir le
nombre de combinaisons possibles pour des groupes de 7 bits 63). En ltendant par la suite 256 carac61 Dans

les versions de Python antrieures la version 3.0, les chanes de caractres de type string taient en
fait des squences doctets (qui pouvaient reprsenter des caractres, mais avec un certain nombre de
limitations assez gnantes). Il existait cependant dj un deuxime type de chanes, le type Unicode pour
traiter les chanes de caractres au sens o nous lentendons dsormais.
62 ASCII = American Standard Code for Information Interchange
63 En fait, on utilisait dj les octets l poque, mais lun des bits de l octet devait tre rserv comme bit de
contrle pour les systmes de rattrapage d erreur. Lamlioration ultrieure de ces systmes permit de

128

Approfondir les structures de donnes

tres, on put ladapter aux exigences du traitement des textes crits dans dautres langues que langlais, mais au prix dune dispersion des normes (ainsi par exemple, la norme ISO-8859-1 (latin-1) codifie
tous les caractres accentus du franais ou de lallemand (entre autres), mais aucun caractre grec,
hbreu ou cyrillique. Pour ces langues, il faudra respectivement utiliser les normes ISO-8859-7,
ISO-8859-8, ISO-8859-5, bien videmment incompatibles, et dautres normes encore pour larabe, le
tchque, le hongrois...
Lintrt rsiduel de ces normes anciennes rside dans leur simplicit. Elles permettent en effet aux
dveloppeurs dapplications informatiques de considrer que chaque caractre typographique est assimilable un octet, et que par consquent une chane de caractres nest rien dautre quune squence
doctets. Cest ainsi que fonctionnait lancien type de donnes string de Python (dans les versions
antrieures la version 3.0).
Toutefois, comme nous lavons dj voqu sommairement au chapitre 5, les applications
informatiques modernes ne peuvent plus se satisfaire de ces normes triques. Il faut dsormais
pouvoir encoder, dans un mme texte, tous les caractres de nimporte quel alphabet de nimporte
quelle langue. Une organisation internationale a donc t cre : le Consortium Unicode, laquelle a
effectivement dvelopp une norme universelle sous le nom de Unicode. Cette nouvelle norme vise
donner tout caractre de nimporte quel systme dcriture de langue un nom et un identifiant
numrique, et ce de manire unifie, quelle que soit la plate-forme informatique ou le logiciel.
Une difficult se prsente, cependant. Se voulant universelle, la norme Unicode doit attribuer un
identifiant numrique diffrent plusieurs dizaines de milliers de caractres. Tous ces identifiants ne
pourront videmment pas tre encods sur un seul octet. premire vue, ils serait donc tentant de
dcrter qu lavenir, chaque caractre devra tre encod sur deux octets (cela ferait 65536
possibilits), ou trois (16777216 possibilits) ou quatre (plus de 4 milliards de possibilits). Chacun de
ces choix rigides entrane cependant son lot dinconvnients. Le premier, commun tous, est que lon
perd la compatibilit avec la multitude de documents informatiques prexistants (et notamment de
logiciels), qui ont t encods aux normes anciennes, sur la base du paradigme un caractre gale un
octet . Le second est li limpossibilit de satisfaire deux exigences contradictoires : si lon se
contente de deux octets, on risque de manquer de possibilits pour identifier des caractres rares ou
des attributs de caractres qui seront probablement souhaits dans lavenir ; si lon impose trois,
quatre octets ou davantage, par contre, on aboutit un monstrueux gaspillage de ressources : la
plupart des textes courants se satisfaisant dun jeu de caractres restreint, limmense majorit de ces
octets ne contiendraient en effet que des zros.
Afin de ne pas se retrouver pige dans un carcan de ce genre, la norme Unicode ne fixe aucune rgle
concernant le nombre doctets ou de bits rserver pour lencodage. Elle spcifie seulement la valeur
numrique de lidentifiant associ chaque caractre. En fonction des besoins, chaque systme
informatique est donc libre dencoder en interne cet identifiant comme bon lui semble, par
exemple sous la forme dun entier ordinaire. Comme tous les langages de programmation modernes,
Python sest donc pourvu dun type de donnes chane de caractres (le type string) qui respecte
scrupuleusement la norme Unicode. La reprsentation interne des codes numriques correspondants
est sans importance pour le programmeur.
librer ce huitime bit pour y stocker de l information utile : cela autorisa lextension du jeu ASCII 256
caractres (normes ISO-8859, etc.).

Le point sur les chanes de caractres

129

Nous verrons un peu plus loin dans ce chapitre quil est effectivement possible de placer dans une
chane de ce type un mlange quelconque de caractres issus dalphabets diffrents (quil sagisse de
caractres ASCII standards, de caractres accentus, de symboles mathmatiques ou de caractres
grecs, cyrilliques, arabes, etc.), et que chacun deux est effectivement reprsent en interne par un
code numrique unique.

Squences doctets : le type bytes


ce stade de nos explications, il devient urgent de prciser encore quelque chose.
Nous avons vu que la norme Unicode ne fixe en fait rien dautre que des valeurs numriques, pour tous
les identifiants standardiss destins dsigner de manire univoque les caractres des alphabets du
monde entier (plus de 240000 en novembre 2005). Elle ne prcise en aucune faon la manire dont ces
valeurs numriques doivent tre encodes concrtement sous forme doctets ou de bits.
Pour le fonctionnement interne des applications informatiques, cela na pas dimportance. Les
concepteurs de langages de programmation, de compilateurs ou dinterprteurs pourront dcider
librement de reprsenter ces caractres par des entiers sur 8, 16, 24, 32, 64 bits, ou mme (bien que
lon nen voie pas lintrt !) par des rels en virgule flottante : cest leur affaire et cela ne nous
concerne pas. Nous ne devons donc pas nous proccuper du format rel des caractres, lintrieur
dune chane string de Python.
Il en va tout autrement, par contre, pour les entres/sorties. Les dveloppeurs que nous sommes
devons absolument pouvoir prciser sous quelle forme exacte les donnes sont attendues par nos
programmes, que ces donnes soient fournies par lintermdiaire de saisies au clavier ou par
importation depuis une source quelconque. De mme, nous devons pouvoir choisir le format des
donnes que nous exportons vers nimporte quel dispositif priphrique, quil sagisse dune
imprimante, dun disque dur, dun cran...
Pour toutes ces entres ou sorties de chanes de caractres, nous devrons donc toujours considrer
quil sagit concrtement de squences doctets, et utiliser divers mcanismes pour convertir ces
squences doctets en chanes de caractres, et vice-versa.
Python met dsormais votre disposition le type de donnes bytes, spcifiquement conu pour traiter
les squences (ou chanes) doctets. Les donnes de type bytes sont trs similaires aux donnes de type
string, la diffrence prs que ce sont strictement des squences doctets, et non des squences de
caractres. Les caractres peuvent bien entendu tre encods en octets, et les octets dcods en
caractres, mais pas de manire univoque : puisquil existe plusieurs normes dencodage/dcodage, la
mme chane string peut tre convertie en plusieurs chanes bytes diffrentes.
titre dexemple64, nous allons effectuer en lignes de commande un petit exercice dcriture/lecture
dun fichier texte, en exploitant quelques possibilits de la fonction open() que nous navions pas
encore rencontres jusquici. Nous veillerons faire cet exercice avec une chane contenant quelques
caractres accentus, ou dautres symboles non-ASCII.
64 Pour

cet exemple, nous supposons que la norme dencodage par dfaut sur votre systme dexploitation est
Utf-8. Si vous travaillez avec un systme dexploitation ancien, utilisant par exemple une norme telle que
CP437, CP850, CP1252 ou ISO8859-1 (Latin-1), les rsultats seront lgrement diffrents en ce qui concerne les
nombres et valeurs des octets, mais vous ne devriez pas avoir de mal interprter ce que vous obtenez.

130
>>>
>>>
>>>
17
>>>

Approfondir les structures de donnes


chaine = "Amlie et Eugne\n"
of =open("test.txt", "w")
of.write(chaine)
of.close()

Avec ces quelques lignes, nous avons enregistr la chane de caractres chaine sous la forme dune
ligne de texte dans un fichier, de la manire habituelle. Effectuons prsent une relecture de ce fichier, mais en veillant ouvrir celui-ci en mode binaire, ce qui peut se faire aisment en transmettant
largument "rb" la fonction open(). Dans ce mode, les octets sont transfrs ltat brut, sans
conversion daucune sorte. La lecture avec read() ne nous fournit donc plus une chane de caractres
comme au chapitre prcdent, mais bien une chane doctets, et la variable qui les accueille est pour
cette raison automatiquement type comme variable du type bytes :
>>> of =open("test.txt", "rb")
>>> octets =of.read()
>>> of.close()
>>> type(octets)
<class 'bytes'>

# "rb" => mode lecture (r) binaire (b)

En procdant ainsi, nous ne rcuprons donc pas notre chane de caractres initiale, mais bien sa
traduction concrte en octets, dans une donne de type bytes. Essayons dafficher cette donne laide
de la fonction print() :
>>> print(octets)
b'Am\xc3\xa9lie et Eug\xc3\xa8ne\n'

Que signifie ce rsultat ? Lorquon lui demande dafficher une donne de type bytes laide la fonction
print(), Python nous en fournit en fait une reprsentation, entre deux apostrophes pour indiquer quil
sagit dune chane, mais celles-ci prcdes dun b minuscule pour spcifier quil sagit dune chane
doctets (bytes), avec les conventions suivantes :

Les octets de valeur numrique comprise entre 32 et 127 sont reprsents par le caractre
correspondant du code ASCII.
Certains octets de valeur numrique infrieure 32 sont reprsents de manire conventionnelle,
comme le caractre de fin de ligne.
Les autres octets sont reprsents par leur valeur hexadcimale, prcde de \x.
Dans notre exemple, on voit que les caractres non accentus de la chane utilise ont t encods
chacun laide dun seul octet correspondant son code ASCII : nous les reconnaissons donc
directement. Les caractres accentus, par contre (ils nexistent pas dans le code ASCII), sont encods
chacun sur deux octets : \xc3 et \xa9 pour le , \xc3 et \xa8 pour le . Cette forme particulire
dencodage correspond la norme Utf-8, que nous dcrirons plus en dtail dans les pages suivantes.
La reprsentation obtenue avec print() nous aide reconnatre notre chane initiale, certes, mais elle
ne nous montre pas assez bien quil sagit en fait doctets. Essayons donc autre chose. Vous savez que
lon peut aussi examiner le contenu dune squence, lment par lment, laide dune boucle de parcours. Voyons ce que cela donne ici :
>>> for oct in octets:
...
print(oct, end =' ')
...
65 109 195 169 108 105 101 32 101 116 32 69 117 103 195 168 110 101 10

Le point sur les chanes de caractres

131

Cette fois, nous voyons trs clairement quil sagit bien doctets : le parcours nous en restitue toutes les
valeurs numriques, en notation dcimale.
Du fait que les caractres accentus sont encods sur deux octets en Utf-8, la fonction len() ne nous
renvoie pas la mme valeur pour la chane de caractres, et pour son quivalent encod en Utf-8 dans
une chane doctets :
>>> len(chaine)
17
>>> len(octets)
19

Les oprations dextraction dlments, de slicing, etc., fonctionnent de manire analogue avec des
donnes de type byte et de type string, quoique avec des rsultats diffrents, bien entendu :
>>>
g
>>>
195

print(chaine[2], chaine[12], "---", chaine[2:12])


--- lie et Eu
print(octets[2], octets[12], "---", octets[2:12])
117 --- b'\xc3\xa9lie et E'

Attention, vous ne pouvez pas enregistrer une chane doctets telle quelle dans un fichier texte.
Exemple :
>>> of =open("test.txt", "w")
>>> of.write(octets)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: must be str, not bytes

Pour enregistrer une squence doctets, il faut toujours ouvrir le fichier en mode binaire, en utilisant
largument "wb" au lieu de "w" dans linstruction open().
Remarquons pour terminer que nous pouvons dfinir une variable de type bytes et lui affecter une
valeur littrale, en utilisant la syntaxe var = b'chane_de_caractres_strictement_ASCII '.

Lencodage Utf-8
Tout ce qui prcde nous indique que la chane de caractres initiale de notre exemple a d tre
automatiquement convertie, lors de son enregistrement dans un fichier, en une chane doctets
encods suivant la norme Utf-8. La squence doctets dont nous avons trait jusquici correspond donc
une forme particulire dencodage numrique, pour la chane de caractres Amlie et Eugne
Mme si cela peut vous paratre premire vue un peu compliqu, dites-vous bien que
malheureusement lencodage idal nexiste pas. En fonction de ce que lon veut en faire, il peut tre
prfrable dencoder un mme texte de plusieurs manires diffrentes. Cest pour cette raison quont
t dfinies, en parallle avec la norme Unicode, plusieurs normes dencodage : Utf-8, Utf-16, Utf-32, et
quelques variantes. Toutes ces normes utilisent les mmes identifiants numriques pour encoder les
caractres, mais elles diffrent sur la manire denregistrer concrtement ces identifiants sous forme
doctets. Ne vous affolez pas, cependant : vous ne serez vraisemblablement jamais confront qu la
premire dentre elles (Utf-8). Les autres ne concerneront que certains spcialistes de domaines pointus .

132

Approfondir les structures de donnes

La norme dencodage Utf-8 est dsormais la norme prfrentielle pour la plupart des textes courants,
parce que :

dune part, elle assure une parfaite compatibilit avec les textes encods en pur ASCII (ce qui
est le cas de nombreux codes sources de logiciels), ainsi quune compatibilit partielle avec les
textes encods laide de ses variantes tendues , telles que Latin-1 ;
dautre part, cette nouvelle norme est celle qui est la plus conome en ressources, tout au moins
pour les textes crits dans une langue occidentale.
Suivant cette norme, les caractres du jeu ASCII standard sont encods sur un seul octet. Les autres
seront encods en gnral sur deux octets, parfois trois ou mme quatre octets pour les caractres les
plus rares.
titre de comparaison, rappelons ici que la norme la plus couramment utilise avant Utf-8 par les
francophones tait la norme Latin-1 (elle est encore largement rpandue, en particulier dans les
environnements de travail Windows sous le nom de CP1252 65). Cette norme permettait dencoder sur
un seul octet un jeu de caractres accentus restreint, correspondant aux principales langues de lEurope occidentale (franais, allemand, portugais, etc.).
Les normes Utf-16 et Utf-32 encodent systmatiquement tous les caractres sur deux octets pour la
premire, et quatre octets pour la seconde. Ces normes ne sont utilises que pour des usages trs
spcifiques, comme pour le traitement interne des chanes de caractres par un interprteur ou un
compilateur. Vous ne les rencontrerez gure.

Conversion (encodage/dcodage) des chanes


Avec les versions de Python antrieures la version 3.0, comme dans beaucoup dautres langages, il
fallait frquemment convertir les chanes de caractres dune norme dencodage une autre. Du fait
des conventions et des mcanismes adopts dsormais, vous ne devrez plus beaucoup vous en
proccuper pour vos propres programmes traitant des donnes rcentes.
Il vous arrivera cependant de devoir convertir des fichiers encods suivant une norme ancienne et/ou
trangre : un programmeur digne de ce nom doit tre capable deffectuer ces conversions. Python
vous fournit fort heureusement les outils ncessaires, sous la forme de mthodes des objets concerns.
Conversion dune chane bytes en chane string
Considrons par exemple la squence doctets obtenue la fin de notre prcdent petit exercice. Si
nous savons que cette squence correspond un texte encod suivant la norme Utf-8, nous pouvons la
dcoder en chane de caractres laide de la mthode decode(), avec largument "Utf-8" (ou
indiffremment "utf-8", "Utf8" ou "utf8") :
>>> ch_car = octets.decode("utf8")
>>> ch_car
'Amlie et Eugne\n'
>>> type(ch_car)
<class 'str'>

65 Dans

CP850.

une fentre dinvite de commandes DOS sous Windows XP, lencodage par dfaut est mme encore

Le point sur les chanes de caractres

133

Le parcours de la chane obtenue nous fournit bien des caractres, cette fois :
>>> for c in ch_car:
...
print(c, end =' ')
...
A m l i e
e t
E u g n e

Conversion dune chane string en chane bytes


Pour convertir une chane de caractres en une squence doctets, encode suivant une norme
particulire, on utilise la mthode encode(), qui fonctionne de manire parfaitement symtrique la
mthode decode() dcrite prcdemment. Convertissons par exemple la mme chane de caractres,
la fois en Utf-8 et en Latin-1 pour comparaison.
>>> chaine = "Bonne fte de Nol"
>>> octets_u = chaine.encode("Utf-8")
>>> octets_l = chaine.encode("Latin-1")
>>> octets_u
b'Bonne f\xc3\xaate de No\xc3\xabl'
>>> octets_l
b'Bonne f\xeate de No\xebl'

Dans les squences doctets ainsi obtenues, on voit clairement que les caractres accentus et sont
encods laide de deux octets dans le cas de la squence Utf-8, et laide dun seul octet dans le cas de
la squence Latin-1.
Conversions automatiques lors du traitement des fichiers
Il vous faut prsent reconsidrer ce qui se passe lorsque vous souhaitez mmoriser des chanes de
caractres dans un fichier texte.
Jusqu prsent en effet, nous navons pas attir votre attention sur le problme que constitue la
norme dencodage de ces chanes, parce que la fonction open() de Python dispose fort heureusement
dun paramtrage par dfaut qui convient la plupart des situations modernes concrtes. Lorsque
vous ouvrez un fichier en criture, par exemple, en choisissant "w" ou "a" comme deuxime argument
pour open(), Python encode automatiquement les chanes enregistrer en suivant la norme par
dfaut de votre systme dexploitation (dans nos exemples, nous avons considr quil sagissait de
Utf-8), et la conversion inverse est effectue lors des oprations de lecture 66. Nous avons donc pu
aborder ltude des fichiers, au chapitre prcdent, sans vous encombrer lesprit avec des explications
trop dtailles.
Dans les exercices des pages prcdentes, nous avons encore exploit sans le dire cette possibilit
offerte par Python. Mais voyons prsent comment enregistrer des textes en leur appliquant un
encodage diffrent de celui qui est prvu par dfaut, ne serait-ce que pour nous assurer que lencodage
ralis est bien celui que nous voulons (nous devons absolument procder ainsi si nous souhaitons que
nos scripts puissent tre utiliss sur diffrents OS).
66 Dans

les versions de Python antrieures la version 3.0, les chanes de caractres devaient toujours tre
converties en squences doctets avant dtre enregistres. Lancien type string tant par ailleurs quivalent
au type bytes actuel, aucune conversion ntait effectue automatiquement lors des oprations de
lecture/criture de fichiers.

134

Approfondir les structures de donnes

La technique est simple. Il suffit dindiquer cet encodage open() laide dun argument
supplmentaire : encoding ="norme_choisie". titre dexemple, nous pouvons refaire les exercices des
pages prcdentes en forant cette fois lencodage en Latin-1 :
>>> chaine ="Amlie et Eugne\n"
>>> of =open("test.txt", "w", encoding ="Latin-1")
>>> of.write(chaine)
17
>>> of.close()
>>> of =open("test.txt", "rb")
>>> octets =of.read()
>>> of.close()
>>> print(octets)
b'Am\xe9lie et Eug\xe8ne\n'

etc.
vous deffectuer divers contrles et essais sur cette squence doctets, si vous le
souhaitez.

Cest pareil lorsque vous ouvrez un fichier en lecture. Par dfaut, Python considre que le fichier est
encod suivant la norme par dfaut de votre systme dexploitation, mais ce nest videmment pas une
certitude. Essayons par exemple de r-ouvrir sans prcaution le fichier test.txt que nous venons de
crer dans les lignes prcdentes :
>>> of =open("test.txt", "r")
>>> ch_lue =of.read()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.1/codecs.py", line 300, in decode
(result, consumed) = self._buffer_decode(data, self.errors, final)
UnicodeDecodeError: 'utf8' codec can't decode bytes in position 2-4:
invalid data

Le message derreur est explicite : supposant que le fichier tait encod en Utf-8, Python na pas pu le
dcoder67. Tout rentre dans lordre si nous prcisons :
>>> of =open("test.txt", "r", encoding ="Latin-1")
>>> ch_lue =of.read()
>>> of.close()
>>> ch_lue
'Amlie et Eugne\n'

Dans les scripts labors, il sera probablement toujours prfrable de prciser lencodage suppos
pour les fichiers que vous traitez, quitte devoir demander cette information lutilisateur, ou
imaginer des tests plus ou moins labors pour le dterminer de faon automatique (ce qui est loin
dtre vident !).
Cas des scripts Python
Les scripts Python que vous crivez sont eux-mmes des textes, bien entendu. Suivant la configuration
de votre logiciel diteur, ou de votre OS, ces textes pourront donc se retrouver encods suivant
67 En

informatique, on appelle codec (codeur/dcodeur) tout dispositif de conversion de format. Vous


rencontrerez par exemple de nombreux codecs dans le monde du multimdia (codecs audio, vido...). Python
dispose de nombreux codecs pour convertir les chanes de caractres suivant les diffrentes normes en
vigueur.

Le point sur les chanes de caractres

135

diffrentes normes. Afin que Python puisse les interprter correctement, il vous est conseill dy
inclure toujours lun des pseudo-commentaires suivants (obligatoirement la 1e ou la 2e ligne) :
# -*- coding:Latin-1 -*-

Ou bien :
# -*- coding:Utf-8 -*-

en indiquant lencodage effectivement utilis, bien videmment !


Ainsi linterprteur Python sait dcoder correctement les chanes de caractres littrales que vous
avez utilises dans le script. Notez que vous pouvez omettre ce pseudo-commentaire si vous tes
certain que vos scripts sont encods en Utf-8, car cest cet encodage qui est dsormais la norme par
dfaut pour les scripts Python68.

Accder dautres caractres que ceux du clavier


Voyons prsent quel parti vous pouvez tirer du fait que tous les caractres possdent leur identifiant
numrique universel Unicode. Pour accder ces identifiants, Python met votre disposition un
certain nombre de fonctions prdfinies.
La fonction ord(ch) accepte nimporte quel caractre comme argument. En retour, elle fournit la
valeur de lidentifiant numrique correspondant ce caractre. Ainsi ord("A") renvoie la valeur 65, et
ord("") renvoie la valeur 296.
La fonction chr(num) fait exactement le contraire, en vous prsentant le caractre typographique
dont lidentifiant Unicode est gal num. Pour que cela fonctionne, il faut cependant que deux
conditions soient ralises :

la valeur de num doit correspondre effectivement un caractre existant (la rpartition des
identifiants unicode nest pas continue : certains codes ne correspondent donc aucun caractre)
votre ordinateur doit disposer dune description graphique du caractre, ou, en dautres termes,
connatre le dessin de ce caractre, que lon appelle un glyphe. Les systmes dexploitation rcents
disposent cependant de bibliothques de glyphes trs tendues, ce qui devrait vous permettre
den afficher des milliers lcran.
Ainsi, par exemple, chr(65) renvoie le caractre A, et chr(1046) renvoie le caractre cyrillique .
Vous pouvez exploiter ces fonctions prdfinies pour vous amuser explorer le jeu de caractres
disponible sur votre ordinateur. Vous pouvez par exemple retrouver les caractres minuscules de
lalphabet grec, en sachant que les codes qui leur sont attribus vont de 945 969. Ainsi le petit script
ci-dessous :
s = ""
# chane vide
i = 945
# premier code
while i <= 969:
# dernier code
s += chr(i)
i = i + 1
print("Alphabet grec (minuscule) : ", s)

devrait afficher le rsultat suivant :


68 Dans les versions de Python antrieures

la version 3.0, lencodage par dfaut tait ASCII.

136

Approfondir les structures de donnes

Alphabet grec (minuscule) :

Exercices
10.14 crivez un petit script qui affiche une table des codes ASCII. Le programme doit afficher tous
les caractres en regard des codes correspondants. partir de cette table, tablissez la relation
numrique simple reliant chaque caractre majuscule au caractre minuscule correspondant.

10.15 Modifiez le script prcdent pour explorer les codes situs entre 128 et 256, o vous
retrouverez nos caractres accentus (parmi de nombreux autres). La relation numrique
trouve dans lexercice prcdent reste-t-elle valable pour les caractres accentus du
franais ?
10.16 partir de cette relation, crivez une fonction qui convertit tous les caractres minuscules en
majuscules, et vice-versa (dans une phrase fournie en argument).
10.17 crivez un script qui recopie un fichier texte en remplaant tous ses espaces par le groupe de
trois caractres -*-. Le fichier copier sera fourni encod selon la norme Latin-1, et le fichier
destinataire devra tre encod en Utf-8. Les noms des 2 fichiers seront demands en dbut de
script.
10.18 crivez une fonction voyelle(car), qui renvoie vrai si le caractre fourni en argument est une
voyelle.
10.19 crivez une fonction compteVoyelles(phrase), qui renvoie le nombre de voyelles contenues
dans une phrase donne.
10.20 Explorez la gamme des caractres Unicode disponibles sur votre ordinateur, laide de boucles
de programmes similaires celle que nous avons nous-mmes utilise pour afficher lalphabet
grec. Trouvez ainsi les codes correspondant lalphabet cyrillique, et crivez un script qui
affiche celui-ci en majuscules et en minuscules.

Les chanes sont des objets


Dans les chapitres prcdents, vous avez dj rencontr de nombreux objets. Vous savez donc que lon
peut agir sur un objet laide de mthodes (cest--dire des fonctions associes cet objet).
Sous Python, les chanes de caractres sont des objets. On peut donc effectuer de nombreux
traitements sur les chanes de caractres en utilisant des mthodes appropries. En voici
quelques-unes, choisies parmi les plus utiles69 :

split() : convertit une chane en une liste de sous-chanes. On peut choisir le caractre sparateur
en le fournissant comme argument, sinon cest un espace par dfaut :
>>> c2 ="Votez pour moi"
>>> a = c2.split()
>>> print(a)
['Votez', 'pour', 'moi']
>>> c4 ="Cet exemple, parmi d'autres, peut encore servir"
69 Il sagit

de quelques exemples seulement. La plupart de ces mthodes peuvent tre utilises avec diffrents
paramtres que nous nindiquons pas tous ici (par exemple, certains paramtres permettent de ne traiter
quune partie de la chane). Vous pouvez obtenir la liste complte de toutes les mthodes associes un objet
laide de la fonction intgre dir(). Veuillez consulter lun ou lautre des ouvrages de rfrence (ou la
documentation en ligne de Python) si vous souhaitez en savoir davantage.

Le point sur les chanes de caractres

137

>>> c4.split(",")
['Cet exemple', " parmi d'autres", ' peut encore servir']

join(liste) : rassemble une liste de chanes en une seule (cette mthode effectue donc laction
inverse de la prcdente). Attention : la chane laquelle on applique cette mthode est celle qui
servira de sparateur (un ou plusieurs caractres) ; largument transmis est la liste des chanes
rassembler :
>>> b =["Bte", "", "manger", "du", "foin"]
> >> print(" ".join(b))
Bte manger du foin
>>> print("---".join(b))
Bte------manger---du---foin

find(sch) : cherche la position dune sous-chane sch dans la chane :


>>> ch1 = "Cette leon vaut bien un fromage, sans doute ?"
>>> ch2 = "fromage"
>>> print(ch1.find(ch2))
25

count(sch) : compte le nombre de sous-chanes sch dans la chane :


>>> ch1 = "Le hron au long bec emmanch d'un long cou"
>>> ch2 = 'long'
>>> print(ch1.count(ch2))
2

lower() : convertit une chane en minuscules :


>>> ch = "CLIMNE est un prnom ancien"
>>> print(ch.lower())
climne est un prnom ancien

upper() : convertit une chane en majuscules :


>>> ch = "Matre Jean-Nol Hbrt"
>>> print(ch.upper())
MATRE JEAN-NOL HBRT

title() : convertit en majuscule linitiale de chaque mot (suivant lusage des titres anglais) :
>>> ch ="albert ren lise vronique"
>>> print(ch.title())
Albert Ren lise Vronique

capitalize() : variante de la prcdente. Convertit en majuscule seulement la premire lettre de la


chane :
>>> b3 = "quel beau temps, aujourdhui !"
>>> print(b3.capitalize())
"Quel beau temps, aujourdhui !"

swapcase() : convertit toutes les majuscules en minuscules, et vice-versa :


>>> ch = "Le Livre Et La Tortue"
>>> print(ch.swapcase())
lE lIVRE eT lA tORTUE

strip() : enlve les espaces ventuels au dbut et la fin de la chane :


>>> ch = "
Monty Python
>>> ch.strip()
'Monty Python'

"

replace(c1, c2) : remplace tous les caractres c1 par des caractres c2 dans la chane :

138

Approfondir les structures de donnes


>>> ch8 = "Si ce n'est toi c'est donc ton frre"
>>> print(ch8.replace(" ","*"))
Si*ce*n'est*toi*c'est*donc*ton*frre

index(car) : retrouve lindice (index) de la premire occurrence du caractre car dans la chane :
>>> ch9 ="Portez ce vieux whisky au juge blond qui fume"
>>> print(ch9.index("w"))
16

Dans la plupart de ces mthodes, il est possible de prciser quelle portion de la chane doit tre traite,
en ajoutant des arguments supplmentaires. Exemples :
>>> print (ch9.index("e"))
4
>>> print (ch9.index("e",5))
8
>>> print (ch9.index("e",15))
29

#
#
#
#
#
#

cherche partir du dbut de la chane


et trouve le premier 'e'
cherche seulement partir de l'indice 5
et trouve le second 'e'
cherche partir du caractre n 15
et trouve le quatrime 'e'

Etc.
Comprenez bien quil nest pas possible de dcrire toutes les mthodes disponibles, ainsi que leur
paramtrage, dans le cadre restreint de ce cours. Si vous souhaitez en savoir davantage, il vous faut
consulter la documentation en ligne de Python (Library reference), ou un bon ouvrage de rfrence.

Fonctions intgres
toutes fins utiles, rappelons galement ici que lon peut aussi appliquer aux chanes un certain
nombre de fonctions intgres dans le langage :

len(ch) renvoie la longueur de la chane ch, ou en dautres termes, son nombre de caractres.
float(ch) convertit la chane ch en un nombre rel (float) (bien entendu, cela ne pourra
fonctionner que si la chane reprsente bien un nombre, rel ou entier) :
>>> a = float("12.36")
>>> print a + 5
17.36

# Attention : pas de virgule dcimale !

int(ch) convertit la chane ch en un nombre entier (avec des restrictions similaires) :


>>> a = int("184")
>>> print a + 20
204

str(obj) convertit (ou reprsente) lobjet obj en une chane de caractres. obj peut tre une
donne d peu prs nimporte quel type :
>>>> a, b = 17, ["mile", 7.65]
>>> ch =str(a) +" est un entier et " +str(b) +" est une liste."
>>> print(ch)
17 est un entier et ['mile', 7.65] est une liste.

Formatage des chanes de caractres


Pour terminer ce tour dhorizon des fonctionnalits associes aux chanes de caractres, il nous parat
judicieux de vous prsenter encore une technique de traitement trs puissante, que lon appelle formatage des chanes. Celle-ci se rvle particulirement utile dans tous les cas o vous devez construire une

Le point sur les chanes de caractres

139

chane de caractres complexe partir dun certain nombre de morceaux, tels que les valeurs de
variables diverses.
Considrons par exemple que vous ayez crit un programme qui traite de la couleur et de la
temprature dune solution aqueuse, en chimie. La couleur est mmorise dans une chane de
caractres nomme coul, et la temprature dans une variable de type rel nomme temp. Vous
souhaitez prsent que votre programme construise une chane de caractres partir de ces donnes,
par exemple une phrase telle que la suivante : La solution est devenue rouge, et sa temprature
atteint 12,7 C .
Vous pouvez construire cette chane en assemblant des morceaux laide de loprateur de
concatnation (le symbole +), mais il vous faudra alors utiliser aussi la fonction intgre str() pour
convertir en chane de caractres la valeur numrique contenue dans la variable de type float (faites
lexercice).
Python vous offre une autre possibilit. Vous pouvez prparer une chane patron contenant
lessentiel du texte invariable, avec des balises particulires aux endroits (les champs) o vous souhaitez
quapparaissent des contenus variables. Vous appliquerez ensuite cette chane la mthode format(),
laquelle vous fournirez comme arguments les divers objets convertir en caractres et insrer en
remplacement des balises. Un exemple vaut certainement mieux quun long discours :
>>> coul ="verte"
>>> temp =1.347 + 15.9
>>> ch ="La couleur est {} et la temprature vaut {} C"
>>> print(ch.format(coul, temp))
La couleur est verte et la temprature vaut 17.247 C

Les balises utiliser sont constitues daccolades, contenant ou non des indications de formatage :

Si les balises sont vides (cas le plus simple), la mthode format() devra recevoir autant
darguments quil y aura de balises dans la chane. Python appliquera alors la fonction str()
chacun de ces arguments, et les insrera ensuite dans la chane la place des balises, dans le
mme ordre. Les arguments peuvent tre nimporte quel objet ou expression Python :
>>> pi =3.1416
>>> r =4.7
>>> ch ="Laire dun disque de rayon {} est gale {}."
>>> print(ch.format(r, pi * r**2))
Laire dun disque de rayon 4.7 est gale 69.397944.

Les balises peuvent contenir des numros dordre (compts partir de zro) pour dsigner
prcisment lesquels des arguments transmis format() devront les remplacer. Cette technique
est particulirement prcieuse si le mme argument doit remplacer plusieurs balises :
>>> phrase ="Le{0} chien{0} aboie{1} et le{0} chat{0} miaule{1}."
>>> print(phrase.format("", ""))
Le chien aboie et le chat miaule.
>>> print(phrase.format("s", "nt"))
Les chiens aboient et les chats miaulent.

Les balises peuvent aussi contenir des indications de formatage (en conjonction ou non avec des
numros dordre). Par exemple, vous pouvez ainsi limiter la prcision du rsultat final, forcer
lutilisation de la notation scientifique, fixer le nombre total de caractres, etc. :
>>> ch ="L'aire d'un disque de rayon {} est gale {:8.2f}."

140

Approfondir les structures de donnes


>>> print(ch.format(r, pi *
L'aire d'un disque de rayon
>>> ch ="L'aire d'un disque
>>> print(ch.format(r, pi *
L'aire d'un disque de rayon

r**2))
4.7 est gale
69.40.
de rayon {0} est gale {1:6.2e}."
r**2))
4.7 est gale 6.94e+01.

Dans le premier essai, le rsultat est format de manire comporter un total de 8 caractres,
dont 2 chiffres aprs le point dcimal. Dans le second, le rsultat est prsent en notation
scientifique (e+01 signifie x 1001). Veuillez constater au passage que les arrondis ventuels sont
effectus correctement.
La description complte de toutes les possibilits de formatage comporterait plusieurs pages, et cela
sortirait largement du cadre de ce livre. Sil vous faut un formatage trs particulier, veuillez consulter
la documentation en ligne de Python, ou des manuels plus spcialiss. Signalons simplement au
passage, que le formatage permet dafficher trs facilement divers rsultats numriques en notation
binaire, octale ou hexadcimale :
>>> n =789
>>> txt ="Le nombre {0:d} (dcimal) vaut {0:x} en hexadcimal et {0:b} en binaire."
>>> print(txt.format(n))
Le nombre 789 (dcimal) vaut 315 en hexadcimal et 1100010101 en binaire.

Formatage des chanes lancienne


Les versions de Python antrieures la version 3.0 utilisaient une technique de formatage lgrement
diffrente et un peu moins labore, qui reste encore utilisable. Il est cependant fortement conseill
dadopter plutt celle que nous avons dcrite dans les paragraphes prcdents. Nous expliquons
sommairement ici lancienne convention, parce que vous risquez de la rencontrer dans les scripts de
nombreux programmeurs (et mme dans quelques-uns de nos exemples !). Elle consiste formater la
chane en assemblant deux lments laide de loprateur %. gauche de cet oprateur, la chane
patron contenant des balises commenant toujours par %, et droite (entre parenthses) le ou les
objets que Python devra insrer dans la chane, en lieu et place des balises.
Exemple :
>>> coul ="verte"
>>> temp = 1.347 + 15.9
>>> print ("La couleur est %s et la temprature vaut %s C" % (coul, temp))
La couleur est verte et la temprature vaut 17.247 C

La balise %s joue le mme rle que {} dans la nouvelle technique. Elle accepte nimporte quel objet
(chane, entier, float, liste...). Vous utilisez aussi dautres balises plus labores, telles que %8.2f, ou
%6.2e, qui correspondent aux {:8.2f} et {:6.2e} de la nouvelle technique. Cest donc quivalent pour
les cas les plus simples, mais soyez persuads que les possibilits de la nouvelle formulation sont
beaucoup plus tendues.
Exercices
10.21 crivez un script qui recopie en Utf-8 un fichier texte encod lorigine en Latin-1, en veillant
en outre ce que chaque mot commence par une majuscule.
Le programme demandera les noms des fichiers lutilisateur. Les oprations de lecture et
dcriture des fichiers auront lieu en mode texte ordinaire.

Le point sur les chanes de caractres

141

10.22 Variante de lexercice prcdent : effectuez les oprations de lecture et dcriture des fichiers
en mode binaire, et les oprations de dcodage/encodage sur les squences doctets. Au
passage, vous traiterez les lignes de manire remplacer tous les espaces par le groupe de
3 caractres -*-.
10.23 crivez un script qui compte le nombre de mots contenus dans un fichier texte.
10.24 crivez un script qui recopie un fichier texte en fusionnant (avec la prcdente) les lignes qui
ne commencent pas par une majuscule.
10.25 Vous disposez dun fichier contenant des valeurs numriques. Considrez que ces valeurs sont
les diamtres dune srie de sphres. crivez un script qui utilise les donnes de ce fichier pour
en crer un autre, organis en lignes de texte qui exprimeront en clair les autres caractristiques de ces sphres (surface de section, surface extrieure et volume), dans des phrases telles
que :
Diam. 46.20 cm Section 1676.39 cm Surf. 6705.54
Diam. 120.00 cm Section 11309.73 cm Surf. 45238.93
Diam.
0.03 cm Section
0.00 cm Surf.
0.00
Diam. 13.90 cm Section
151.75 cm Surf.
606.99
Diam. 88.80 cm Section 6193.21 cm Surf. 24772.84
etc.

cm
cm
cm
cm
cm

Vol. 51632.67 cm
Vol. 904778.68 cm
Vol.
0.00 cm
Vol.
1406.19 cm
Vol. 366638.04 cm

10.26 Vous avez votre disposition un fichier texte dont les lignes reprsentent des valeurs
numriques de type rel, sans exposant (et encodes sous forme de chanes de caractres).
crivez un script qui recopie ces valeurs dans un autre fichier, en les arrondissant de telle
sorte que leur partie dcimale ne comporte plus quun seul chiffre aprs la virgule, ce chiffre
ne pouvant tre que 0 ou 5 (larrondi doit tre correct).

Le point sur les listes


Nous avons dj rencontr les listes plusieurs reprises, depuis leur prsentation sommaire au
chapitre 5. Les listes sont des collections ordonnes dobjets. Comme les chanes de caractres, les
listes font partie dun type gnral que lon appelle squences sous Python. Comme les caractres dans
une chane, les objets placs dans une liste sont rendus accessibles par lintermdiaire dun index (un
nombre qui indique lemplacement de lobjet dans la squence).

Dfinition dune liste accs ses lments


Vous savez dj que lon dlimite une liste laide de crochets :
>>> nombres = [5, 38, 10, 25]
>>> mots = ["jambon", "fromage", "confiture", "chocolat"]
>>> stuff = [5000, "Brigitte", 3.1416, ["Albert", "Ren", 1947]]

Dans le dernier exemple ci-dessus, nous avons rassembl un entier, une chane, un rel et mme une
liste, pour vous rappeler que lon peut combiner dans une liste des donnes de nimporte quel type, y
compris des listes, des dictionnaires et des tuples (ceux-ci seront tudis plus loin).
Pour accder aux lments dune liste, on utilise les mmes mthodes (index, dcoupage en tranches)
que pour accder aux caractres dune chane :

142

Approfondir les structures de donnes

>>> print(nombres[2])
10
>>> print(nombres[1:3])
[38, 10]
>>> print(nombres[2:3])
[10]
>>> print(nombres[2:])
[10, 25]
>>> print(nombres[:2])
[5, 38]
>>> print(nombres[-1])
25
>>> print(nombres[-2])
10

Les exemples ci-dessus devraient attirer votre attention sur le fait quune tranche dcoupe dans une
liste est toujours elle-mme une liste (mme sil sagit dune tranche qui ne contient quun seul lment, comme dans notre troisime exemple), alors quun lment isol peut contenir nimporte quel
type de donne. Nous allons approfondir cette distinction tout au long des exemples suivants.

Les listes sont modifiables


Contrairement aux chanes de caractres, les listes sont des squences modifiables. Cela nous permettra
de construire plus tard des listes de grande taille, morceau par morceau, dune manire dynamique
(cest--dire laide dun algorithme quelconque). Exemples :
>>> nombres[0] = 17
>>> nombres
[17, 38, 10, 25]

Dans lexemple ci-dessus, on a remplac le premier lment de la liste nombres, en utilisant loprateur [ ] (oprateur dindiage) la gauche du signe gale.
Si lon souhaite accder un lment faisant partie dune liste, elle-mme situe dans une autre liste, il
suffit dindiquer les deux index entre crochets successifs :
>>> stuff[3][1] = "Isabelle"
>>> stuff
[5000, 'Brigitte', 3.1415999999999999, ['Albert', 'Isabelle', 1947]]

Comme cest le cas pour toutes les squences, il ne faut jamais oublier que la numrotation des
lments commence partir de zro. Ainsi, dans lexemple ci-dessus on remplace llment no 1 dune
liste, qui est elle-mme llment no 3 dune autre liste : la liste stuff.

Les listes sont des objets


Sous Python, les listes sont des objets part entire, et vous pouvez donc leur appliquer un certain
nombre de mthodes particulirement efficaces. En voici quelques-unes :
>>> nombres = [17, 38, 10, 25, 72]
>>> nombres.sort()
>>> nombres
[10, 17, 25, 38, 72]
>>> nombres.append(12)
>>> nombres

# trier la liste

# ajouter un lment la fin

Le point sur les listes

143

[10, 17, 25, 38, 72, 12]


>>> nombres.reverse()
>>> nombres
[12, 72, 38, 25, 17, 10]

# inverser l'ordre des lments

>>> nombres.index(17)
4

# retrouver l'index d'un lment

>>> nombres.remove(38)
>>> nombres
[12, 72, 25, 17, 10]

# enlever (effacer) un lment

En plus de ces mthodes, vous disposez encore de linstruction intgre del, qui vous permet deffacer
un ou plusieurs lments partir de leur(s) index :
>>> del nombres[2]
>>> nombres
[12, 72, 17, 10]
>>> del nombres[1:3]
>>> nombres
[12, 10]

Notez bien la diffrence entre la mthode remove() et linstruction del : del travaille avec un index ou
une tranche dindex, tandis que remove() recherche une valeur (si plusieurs lments de la liste
possdent la mme valeur, seul le premier est effac).
Exercices
10.27 crivez un script qui gnre la liste des carrs et des cubes des nombres de 20 40.

10.28 crivez un script qui cre automatiquement la liste des sinus des angles de 0 90, par pas de
5. Attention : la fonction sin() du module math considre que les angles sont fournis en radians (360 = 2 radians).
10.29 crivez un script qui permette dobtenir lcran les 15 premiers termes des tables de
multiplication par 2, 3, 5, 7, 11, 13, 17, 19 (ces nombres seront placs au dpart dans une liste)
sous la forme dune table similaire la suivante :
2
3
5

4
6
10

6
9
15

8
12
20

10
15
25

12
18
30

14
21
35

16
24
40

18
27
45

20
30
50

22
33
55

24
36
60

26
39
65

28
42
70

30
45
75

etc.
10.30 Soit la liste suivante :

[Jean-Michel, Marc, Vanessa, Anne, Maximilien,


Alexandre-Benot, Louise]

crivez un script qui affiche chacun de ces noms avec le nombre de caractres correspondant.
10.31 Vous disposez dune liste de nombres entiers quelconques, certains dentre eux tant prsents
en plusieurs exemplaires. crivez un script qui recopie cette liste dans une autre, en omettant
les doublons. La liste finale devra tre trie.
10.32 crivez un script qui recherche le mot le plus long dans une phrase donne (lutilisateur du
programme doit pouvoir entrer une phrase de son choix).
10.33 crivez un script capable dafficher la liste de tous les jours dune anne imaginaire, laquelle
commencerait un jeudi. Votre script utilisera lui-mme trois listes : une liste des noms de jours

144

Approfondir les structures de donnes


de la semaine, une liste des noms des mois, et une liste des nombres de jours que comportent
chacun des mois (ne pas tenir compte des annes bissextiles).
Exemple de sortie :
jeudi 1 janvier

vendredi 2 janvier

samedi 3 janvier

dimanche 4 janvier

... et ainsi de suite, jusquau jeudi 31 dcembre.


10.34 Vous avez votre disposition un fichier texte qui contient un certain nombre de noms
dlves. crivez un script qui effectue une copie trie de ce fichier.
10.35 crivez une fonction permettant de trier une liste. Cette fonction ne pourra pas utiliser la
mthode intgre sort() de Python : vous devez donc dfinir vous-mme lalgorithme de tri.

Techniques de slicing avanc pour modifier une liste


Comme nous venons de le signaler, vous pouvez ajouter ou supprimer des lments dans une liste en
utilisant une instruction (del) et une mthode (append()) intgres. Si vous avez bien assimil le
principe du dcoupage en tranches (slicing), vous pouvez cependant obtenir les mmes rsultats
laide du seul oprateur [ ]. Lutilisation de cet oprateur est un peu plus dlicate que celle dinstructions ou de mthodes ddies, mais elle permet davantage de souplesse :
Insertion dun ou plusieurs lments nimporte o dans une liste
>>> mots = ['jambon', 'fromage', 'confiture', 'chocolat']
>>> mots[2:2] =["miel"]
>>> mots
['jambon', 'fromage', 'miel', 'confiture', 'chocolat']
>>> mots[5:5] =['saucisson', 'ketchup']
>>> mots
['jambon', 'fromage', 'miel', 'confiture', 'chocolat', 'saucisson', 'ketchup']

Pour utiliser cette technique, vous devez prendre en compte les particularits suivantes :

Si vous utilisez loprateur [ ] la gauche du signe gale pour effectuer une insertion ou une
suppression dlment(s) dans une liste, vous devez obligatoirement y indiquer une tranche
dans la liste cible (cest--dire deux index runis par le symbole : ), et non un lment isol dans
cette liste.
Llment que vous fournissez la droite du signe gale doit lui-mme tre une liste. Si vous ninsrez quun seul lment, il vous faut donc le prsenter entre crochets pour le transformer dabord
en une liste dun seul lment. Notez bien que llment mots[1] nest pas une liste (cest la chane
fromage ), alors que llment mots[1:3] en est une.
Vous comprendrez mieux ces contraintes en analysant ce qui suit :
Suppression / remplacement dlments
>>> mots[2:5] = []
# [] dsigne une liste vide
>>> mots
['jambon','fromage','saucisson', 'ketchup']
>>> mots[1:3] = ['salade']
>>> mots
['jambon', 'salade', 'ketchup']

Le point sur les listes

145

>>> mots[1:] = ['mayonnaise', 'poulet', 'tomate']


>>> mots
['jambon', 'mayonnaise', 'poulet', 'tomate']

la premire ligne de cet exemple, nous remplaons la tranche [2:5] par une liste vide, ce qui
correspond un effacement.
la quatrime ligne, nous remplaons une tranche par un seul lment. Notez encore une fois que
cet lment doit lui-mme tre prsent comme une liste.
la 7e ligne, nous remplaons une tranche de deux lments par une autre qui en comporte 3.

Cration dune liste de nombres laide de la fonction range()


Si vous devez manipuler des squences de nombres, vous pouvez les crer trs aisment laide de
cette fonction intgre. Elle renvoie une squence dentiers 70 que vous pouvez utiliser directement, ou
convertir en une liste avec la fonction list(), ou convertir en tuple avec la fonction tuple() (les tuples
seront dcrits un peu plus loin) :
>>> list(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

La fonction range() gnre par dfaut une squence de nombres entiers de valeurs croissantes, et
diffrant dune unit. Si vous appelez range() avec un seul argument, la liste contiendra un nombre de
valeurs gal largument fourni, mais en commenant partir de zro (cest--dire que range(n) gnre
les nombres de 0 n-1). Notez bien que largument fourni nest jamais dans la liste gnre.
On peut aussi utiliser range() avec deux, ou mme trois arguments spars par des virgules, afin de
gnrer des squences de nombres plus spcifiques :
>>>
[5,
>>>
[3,

list(range(5,13))
6, 7, 8, 9, 10, 11, 12]
list(range(3,16,3))
6, 9, 12, 15]

Si vous avez du mal assimiler lexemple ci-dessus, considrez que range() attend toujours de un
trois arguments, que lon pourrait intituler FROM, TO et STEP. FROM est la premire valeur gnrer,
TO est la dernire (ou plutt la dernire + un), et STEP le pas sauter pour passer dune valeur la
suivante. Sils ne sont pas fournis, les paramtres FROM et STEP prennent leurs valeurs par dfaut, qui
sont respectivement 0 et 1.
Les arguments ngatifs sont autoriss :
>>> list(range(10, -10, -3))
[10, 7, 4, 1, -2, -5, -8]

Parcours dune liste laide de for, range() et len()


Linstruction for est linstruction idale pour parcourir une liste :
70 range()

donne en ralit accs un itrateur (un objet Python gnrateur de squences), mais la
description des itrateurs sort du cadre que nous nous sommes fixs pour cet ouvrage dinitiation. Veuillez
donc consulter la bibliographie, page 10, ou la documentation en ligne de Python, si vous souhaitez des
claircissements.

146

Approfondir les structures de donnes

>>> prov = ['La','raison','du','plus','fort','est','toujours','la','meilleure']


>>> for mot in prov:
...
print(mot, end =' ')
...
La raison du plus fort est toujours la meilleure

Si vous voulez parcourir une gamme dentiers, la fonction range() simpose :


>>> for n in range(10, 18, 3):
...
print(n, n**2, n**3)
...
10 100 1000
13 169 2197
16 256 4096

Il est trs pratique de combiner les fonctions range() et len() pour obtenir automatiquement tous les
indices dune squence (liste ou chane). Exemple :
fable = ['Matre','Corbeau','sur','un','arbre','perch']
for index in range(len(fable)):
print(index, fable[index])

Lexcution de ce script donne le rsultat :


0
1
2
3
4
5

Matre
Corbeau
sur
un
arbre
perch

Une consquence importante du typage dynamique


Comme nous lavons dj signal plus haut (page 125), le type de la variable utilise avec linstruction
for est redfini continuellement au fur et mesure du parcours : mme si les lments dune liste sont de
types diffrents, on peut parcourir cette liste laide de for sans quil ne sensuive une erreur, car le
type de la variable de parcours sadapte automatiquement celui de llment en cours de lecture.
Exemple :
>>> divers = [3, 17.25, [5, 'Jean'], 'Linux is not Windoze']
>>> for item in divers:
...
print(item, type(item))
...
3 <class 'int'>
17.25 <class 'float'>
[5, 'Jean'] <class 'list'>
Linux is not Windoze <class 'str'>

Dans lexemple ci-dessus, on utilise la fonction intgre type() pour montrer que la variable item
change effectivement de type chaque itration (ceci est rendu possible grce au typage dynamique des
variables Python).

Oprations sur les listes


On peut appliquer aux listes les oprateurs + (concatnation) et * (multiplication) :
>>> fruits = ['orange','citron']
>>> legumes = ['poireau','oignon','tomate']

Le point sur les listes


>>> fruits
['orange',
>>> fruits
['orange',

147

+ legumes
'citron', 'poireau', 'oignon', 'tomate']
* 3
'citron', 'orange', 'citron', 'orange', 'citron']

Loprateur * est particulirement utile pour crer une liste de n lments identiques :
>>> sept_zeros = [0]*7
>>> sept_zeros
[0, 0, 0, 0, 0, 0, 0]

Supposons par exemple que vous voulez crer une liste B qui contienne le mme nombre dlments
quune autre liste A. Vous pouvez obtenir ce rsultat de diffrentes manires, mais lune des plus
simples consistera effectuer : B = [0]*len(A).

Test dappartenance
Vous pouvez aisment dterminer si un lment fait partie dune liste laide de linstruction in (cette
instruction puissante peut tre utilise avec toutes les squences) :
>>> v = 'tomate'
>>> if v in legumes:
...
print('OK')
...
OK

Copie dune liste


Considrons que vous disposez dune liste fable que vous souhaitez recopier dans une nouvelle
variable que vous appellerez phrase. La premire ide qui vous viendra lesprit sera certainement
dcrire une simple affectation telle que :
>>> phrase = fable

En procdant ainsi, sachez que vous ne crez pas une vritable copie. la suite de cette instruction, il
nexiste toujours quune seule liste dans la mmoire de lordinateur. Ce que vous avez cr est
seulement une nouvelle rfrence vers cette liste. Essayez par exemple :
>>> fable = ['Je','plie','mais','ne','romps','point']
>>> phrase = fable
>>> fable[4] ='casse'
>>> phrase
['Je', 'plie', 'mais', 'ne', 'casse', 'point']

Si la variable phrase contenait une vritable copie de la liste, cette copie serait indpendante de loriginal et ne devrait donc pas pouvoir tre modifie par une instruction telle que celle de la troisime
ligne, qui sapplique la variable fable. Vous pouvez encore exprimenter dautres modifications, soit
au contenu de fable, soit au contenu de phrase. Dans tous les cas, vous constaterez que les
modifications de lune sont rpercutes dans lautre, et vice-versa.
En fait, les noms fable et phrase dsignent tous deux un seul et mme objet en mmoire. Pour dcrire
cette situation, les informaticiens diront que le nom phrase est un alias du nom fable.

148

Approfondir les structures de donnes

Nous verrons plus tard lutilit des alias. Pour linstant, nous voudrions disposer dune technique pour
effectuer une vritable copie dune liste. Avec les notions vues prcdemment, vous devriez pouvoir
en trouver une par vous-mme.
Petite remarque concernant la syntaxe
Python vous autorise tendre une longue instruction sur plusieurs lignes, si vous continuez
encoder quelque chose qui est dlimit par une paire de parenthses, de crochets ou daccolades. Vous
pouvez traiter ainsi des expressions parenthses, ou encore la dfinition de longues listes, de grands
tuples ou de grands dictionnaires (voir plus loin). Le niveau dindentation na pas dimportance : linterprteur dtecte la fin de linstruction l o la paire syntaxique est referme.
Cette fonctionnalit vous permet damliorer la lisibilit de vos programmes. Exemple :
couleurs = ['noir', 'brun', 'rouge',
'orange', 'jaune', 'vert',
'bleu', 'violet', 'gris', 'blanc']

Exercices
10.36 Soient les listes suivantes :
t1 = [31,28,31,30,31,30,31,31,30,31,30,31]
t2 = ['Janvier','Fvrier','Mars','Avril','Mai','Juin',
'Juillet','Aot','Septembre','Octobre','Novembre','Dcembre']

crivez un petit programme qui insre dans la seconde liste tous les lments de la premire,
de telle sorte que chaque nom de mois soit suivi du nombre de jours correspondant :
['Janvier',31,'Fvrier',28,'Mars',31, etc.].
10.37 Crez une liste A contenant quelques lments. Effectuez une vraie copie de cette liste dans une
nouvelle variable B. Suggestion : crez dabord une liste B de mme taille que A mais ne
contenant que des zros. Remplacez ensuite tous ces zros par les lments tirs de A.
10.38 Mme question, mais autre suggestion : crez dabord une liste B vide. Remplissez-la ensuite
laide des lments de A ajouts lun aprs lautre.
10.39 Mme question, autre suggestion encore : pour crer la liste B, dcoupez dans la liste A une
tranche incluant tous les lments ( laide de loprateur [:]).

Le point sur les listes

149

10.40 Un nombre premier est un nombre qui nest divisible que par un et par lui-mme. crivez un
programme qui tablit la liste de tous les nombres premiers compris entre 1 et 1000, en
utilisant la mthode du crible dEratosthne :
Crez une liste de 1000 lments, chacun initialis la valeur 1.
Parcourez cette liste partir de llment dindice 2 : si llment analys possde la valeur
1, mettez zro tous les autres lments de la liste, dont les indices sont des multiples
entiers de lindice auquel vous tes arriv.
Lorsque vous aurez parcouru ainsi toute la liste, les indices des lments qui seront rests 1
seront les nombres premiers recherchs.
En effet : A partir de lindice 2, vous annulez tous les lments dindices pairs : 4, 6, 8, 10, etc.
Avec lindice 3, vous annulez les lments dindices 6, 9, 12, 15, etc., et ainsi de suite. Seuls
resteront 1 les lments dont les indices sont effectivement des nombres premiers.

Nombres alatoires histogrammes


La plupart des programmes dordinateur font exactement la mme chose chaque fois quon les
excute. De tels programmes sont dits dterministes. Le dterminisme est certainement une bonne
chose : nous voulons videmment quune mme srie de calculs applique aux mmes donnes
initiales aboutisse toujours au mme rsultat. Pour certaines applications, cependant, nous pouvons
souhaiter que lordinateur soit imprvisible. Le cas des jeux constitue un exemple vident, mais il en
existe bien dautres.
Contrairement aux apparences, il nest pas facile du tout dcrire un algorithme qui soit rellement
non-dterministe (cest--dire qui produise un rsultat totalement imprvisible). Il existe cependant
des techniques mathmatiques permettant de simuler plus ou moins bien leffet du hasard. Des livres
entiers ont t crits sur les moyens de produire ainsi un hasard de bonne qualit . Nous nallons
videmment pas dvelopper ici une telle question.
Dans son module random, Python propose toute une srie de fonctions permettant de gnrer des
nombres alatoires qui suivent diffrentes distributions mathmatiques. Nous nexaminerons ici que
quelques-unes dentre elles. Veuillez consulter la documentation en ligne pour dcouvrir les autres.
Vous pouvez importer toutes les fonctions du module par :
>>> from random import *

La fonction random du module random permet de crer une liste de nombres rels alatoires, de valeur
comprise entre zro et un. Largument fournir est la taille de la liste :
>>> def list_aleat(n):
...
s = [0]*n
...
for i in range(n):
...
s[i] =random()
...
return s
...
>>> list_aleat(3)
[0.37584811062278767, 0.03459750519478866, 0.714564337038124]
>>> list_aleat(3)
[0.8151025790264931, 0.3772866844634689, 0.8207328556071652]

Vous pouvez constater que nous avons pris le parti de construire dabord une liste de zros de taille n,
et ensuite de remplacer les zros par des nombres alatoires.

150

Approfondir les structures de donnes

Exercices
10.41 Rcrivez la fonction list_aleat() ci-dessus, en utilisant la mthode append() pour construire
la liste petit petit partir dune liste vide (au lieu de remplacer les zros dune liste
prexistante comme nous lavons fait).

10.42 crivez une fonction imprime_liste() qui permette dafficher ligne par ligne tous les lments
contenus dans une liste de taille quelconque. Le nom de la liste sera fourni en argument.
Utilisez cette fonction pour imprimer la liste de nombres alatoires gnrs par la fonction
list_aleat(). Ainsi par exemple, linstruction imprime_liste(liste_aleat(8)) devra afficher
une colonne de 8 nombres rels alatoires.
Les nombres ainsi gnrs sont-ils vraiment alatoires ? Cest difficile dire. Si nous ne tirons quun
petit nombre de valeurs, nous ne pouvons rien vrifier. Par contre, si nous utilisons un grand nombre
de fois la fonction random(), nous nous attendons ce que la moiti des valeurs produites soient plus
grandes que 0,5 (et lautre moiti plus petites).
Affinons ce raisonnement. Les valeurs tires sont toujours dans lintervalle 0-1. Partageons cet
intervalle en 4 fractions gales : de 0 0,25 , de 0,25 0,5 , de 0,5 0,75 , et de 0,75 1.
Si nous tirons un grand nombre de valeurs au hasard, nous nous attendons ce quil y en ait autant
qui se situent dans chacune de nos 4 fractions. Et nous pouvons gnraliser ce raisonnement un
nombre quelconque de fractions, du moment quelles soient gales.
Exercice
10.43 Vous allez crire un programme destin vrifier le fonctionnement du gnrateur de
nombres alatoires de Python en appliquant la thorie expose ci-dessus. Votre programme
devra donc :

Demander lutilisateur le nombre de valeurs tirer au hasard laide de la fonction random(). Il serait intressant que le programme propose un nombre par dfaut (1000 par
exemple).
Demander lutilisateur en combien de fractions il souhaite partager lintervalle des valeurs
possibles (cest--dire lintervalle de 0 1). Ici aussi, il faudrait proposer un nombre de fractions par dfaut (5 par exemple). Vous pouvez galement limiter le choix de lutilisateur
un nombre compris entre 2 et le 1/10e du nombre de valeurs tires au hasard.
Construire une liste de N compteurs (N tant le nombre de fractions souhaites). Chacun
deux sera videmment initialis zro.
Tirer au hasard toutes les valeurs demandes, laide de la fonction random() , et
mmoriser ces valeurs dans une liste.
Mettre en uvre un parcours de la liste des valeurs tires au hasard (boucle), et effectuer un
test sur chacune delles pour dterminer dans quelle fraction de lintervalle 0-1 elle se situe.
Incrmenter de une unit le compteur correspondant.
Lorsque cest termin, afficher ltat de chacun des compteurs.
Exemple de rsultats affichs par un programme de ce type :
Nombre de valeurs tirer au hasard (dfaut = 1000) : 100
Nombre de fractions dans l'intervalle 0-1 (entre 2 et 10, dfaut =5) : 5
Tirage au sort des 100 valeurs ...
Comptage des valeurs dans chacune des 5 fractions ...

Le point sur les listes

151

11 30 25 14 20
Nombre de valeurs tirer au hasard (dfaut = 1000) : 10000
Nombre de fractions dans l'intervalle 0-1 (entre 2 et 1000, dfaut =5) : 5
Tirage au sort des 10000 valeurs ...
Comptage des valeurs dans chacune des 5 fractions ...
1970 1972 2061 1935 2062

Une bonne approche de ce genre de problme consiste essayer dimaginer quelles fonctions simples
vous pourriez crire pour rsoudre lune ou lautre partie du problme, puis de les utiliser dans un
ensemble plus vaste.
Par exemple, vous pourriez chercher dfinir dabord une fonction numeroFraction() qui servirait
dterminer dans quelle fraction de lintervalle 0-1 une valeur tire se situe. Cette fonction attendrait
deux arguments (la valeur tire, le nombre de fractions choisi par lutilisateur) et fournirait en retour
lindex du compteur incrmenter (cest--dire le no de la fraction correspondante). Il existe peut-tre
un raisonnement mathmatique simple qui permette de dterminer lindex de la fraction partir de
ces deux arguments. Pensez notamment la fonction intgre int(), qui permet de convertir un
nombre rel en nombre entier en liminant sa partie dcimale.
Si vous ne trouvez pas, une autre rflexion intressante serait peut-tre de construire dabord une
liste contenant les valeurs pivots qui dlimitent les fractions retenues (par exemple 0 0,25 0,5
0,75 - 1 dans le cas de 4 fractions). La connaissance de ces valeurs faciliterait peut-tre lcriture de la
fonction numeroFraction() que nous souhaitons mettre au point.
Si vous disposez dun temps suffisant, vous pouvez aussi raliser une version graphique de ce
programme, qui prsentera les rsultats sous la forme dun histogramme (diagramme en btons ).

Tirage au hasard de nombres entiers


Lorsque vous dvelopperez des projets personnels, il vous arrivera frquemment de souhaiter disposer
dune fonction qui permette de tirer au hasard un nombre entier entre certaines limites. Par exemple,
si vous voulez crire un programme de jeu dans lequel des cartes jouer sont tires au hasard ( partir
dun jeu ordinaire de 52 cartes), vous aurez certainement lutilit dune fonction capable de tirer au
hasard un nombre entier compris entre 1 et 52.
Vous pouvez pour ce faire utiliser la fonction randrange() du module random. Cette fonction peut
tre utilise avec 1, 2 ou 3 arguments.
Avec un seul argument, elle renvoie un entier compris entre zro et la valeur de largument diminu
dune unit. Par exemple, randrange(6) renvoie un nombre compris entre 0 et 5.
Avec deux arguments, le nombre renvoy est compris entre la valeur du premier argument et la valeur du
second argument diminu dune unit. Par exemple, randrange(2, 8) renvoie un nombre compris entre 2 et
7.
Si lon ajoute un troisime argument, celui-ci indique que le nombre tir au hasard doit faire partie
dune srie limite dentiers, spars les uns des autres par un certain intervalle, dfini lui-mme par
ce troisime argument. Par exemple, randrange(3, 13, 3) renverra un des nombres de la srie 3, 6, 9,
12 :
>>> from random import randrange
>>> for i in range(15):

152

Approfondir les structures de donnes

...
print(randrange(3, 13, 3), end =' ')
...
12 6 12 3 3 12 12 12 9 3 9 3 9 3 12

Exercices
10.44 crivez un script qui tire au hasard des cartes jouer. Le nom de la carte tire doit tre
correctement prsent, en clair . Le programme affichera par exemple :
Frappez <Enter>
Dix de Trfle
Frappez <Enter>
As de Carreau
Frappez <Enter>
Huit de Pique
Frappez <Enter>

etc.

pour tirer une carte :


pour tirer une carte :
pour tirer une carte :
pour tirer une carte :

Les tuples
Nous avons tudi jusquici deux types de donnes composites : les chanes, qui sont composes de
caractres, et les listes, qui sont composes dlments de nimporte quel type. Vous devez vous
rappeler une autre diffrence importante entre chanes et listes : il nest pas possible de changer les
caractres au sein dune chane existante, alors que vous pouvez modifier les lments dune liste. En
dautres termes, les listes sont des squences modifiables, alors que les chanes de caractres sont des
squences non-modifiables. Exemple :
>>> liste =['jambon','fromage','miel','confiture','chocolat']
>>> liste[1:3] =['salade']
>>> print(liste)
['jambon', 'salade', 'confiture', 'chocolat']
>>> chaine ='Romo prfre Juliette'
>>> chaine[14:] ='Brigitte'
***** ==> Erreur: object doesn't support slice assignment

*****

Nous essayons de modifier la fin de la chane de caractres, mais cela ne marche pas. La seule
possibilit darriver nos fins est de crer une nouvelle chane, et dy recopier ce que nous voulons
changer :
>>> chaine = chaine[:14] +'Brigitte'
>>> print(chaine)
Romo prfre Brigitte

Python propose un type de donnes appel tuple71, qui est assez semblable une liste mais qui, comme
les chanes, nest pas modifiable.
Du point de vue de la syntaxe, un tuple est une collection dlments spars par des virgules :
>>> tup = 'a', 'b', 'c', 'd', 'e'
>>> print(tup)
('a', 'b', 'c', 'd', 'e')

71 Ce terme nest pas

un mot anglais ordinaire : il sagit dun nologisme informatique.

Les tuples

153

Bien que cela ne soit pas ncessaire, il est vivement conseill de mettre le tuple en vidence en l enfermant dans une paire de parenthses, comme la fonction print() de Python le fait elle-mme. Il sagit
simplement damliorer la lisibilit du code, mais vous savez que cest important.
>>> tup = ('a', 'b', 'c', 'd', 'e')

Oprations sur les tuples


Les oprations que lon peut effectuer sur des tuples sont syntaxiquement similaires celles que lon
effectue sur les listes, si ce nest que les tuples ne sont pas modifiables :
>>> print(tup[2:4])
('c', 'd')
>>> tup[1:3] = ('x', 'y')
==> ***** erreur ! *****
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> tup = ('Andr',) + tup[1:]
>>> print(tup)
('Andr', 'b', 'c', 'd', 'e')

Remarquez quil faut toujours au moins une virgule pour dfinir un tuple (le dernier exemple ci-dessus
utilise un tuple contenant un seul lment : 'Andr').
Vous pouvez dterminer la taille dun tuple laide de len(), le parcourir laide dune boucle for,
utiliser linstruction in pour savoir si un lment donn en fait partie, etc., exactement comme vous le
faites pour une liste. Les oprateurs de concatnation et de multiplication fonctionnent aussi. Mais
puisque les tuples ne sont pas modifiables, vous ne pouvez pas utiliser avec eux, ni lintruction del ni
la mthode remove() :
>>> tu1, tu2 = ("a","b"), ("c","d","e")
>>> tu3 = tu1*4 + tu2
>>> tu3
('a', 'b', 'a', 'b', 'a', 'b', 'a', 'b', 'c', 'd', 'e')
>>> for e in tu3:
...
print(e, end=":")
...
a:b:a:b:a:b:a:b:c:d:e:
>>> del tu3[2]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object doesn't support item deletion

Vous comprendrez lutilit des tuples petit petit. Signalons simplement ici quils sont prfrables aux
listes partout o lon veut tre certain que les donnes transmises ne soient pas modifies par erreur
au sein dun programme. En outre, les tuples sont moins gourmands en ressources systme (ils
occupent moins de place en mmoire, et peuvent tre traits plus rapidement par linterprteur).

Les dictionnaires
Les types de donnes composites que nous avons abords jusqu prsent (chanes, listes et tuples)
taient tous des squences, cest--dire des suites ordonnes dlments. Dans une squence, il est facile
daccder un lment quelconque laide dun index (un nombre entier), mais la condition
expresse de connatre son emplacement.

154

Approfondir les structures de donnes

Les dictionnaires que nous dcouvrons ici constituent un autre type composite. Ils ressemblent aux
listes dans une certaine mesure (ils sont modifiables comme elles), mais ce ne sont pas des squences.
Les lments que nous allons y enregistrer ne seront pas disposs dans un ordre immuable. En
revanche, nous pourrons accder nimporte lequel dentre eux laide dun index spcifique que lon
appellera une cl, laquelle pourra tre alphabtique, numrique, ou mme dun type composite sous
certaines conditions.
Comme dans une liste, les lments mmoriss dans un dictionnaire peuvent tre de nimporte quel
type. Ce peuvent tre des valeurs numriques, des chanes, des listes, des tuples, des dictionnaires, et
mme aussi des fonctions, des classes ou des instances (voir plus loin)72.

Cration dun dictionnaire


titre dexemple, nous allons crer un dictionnaire de langue, pour la traduction de termes
informatiques anglais en franais.
Puisque le type dictionnaire est un type modifiable, nous pouvons commencer par crer un dictionnaire
vide, puis le remplir petit petit. Du point de vue de la syntaxe, on reconnat un dictionnaire au fait
que ses lments sont enferms dans une paire daccolades. Un dictionnaire vide sera donc not { } :
>>>
>>>
>>>
>>>

dico = {}
dico['computer'] = 'ordinateur'
dico['mouse'] ='souris'
dico['keyboard'] ='clavier'

>>> print(dico)
{'computer': 'ordinateur', 'keyboard': 'clavier', 'mouse': 'souris'}

Comme vous pouvez lobserver dans la dernire ligne ci-dessus, un dictionnaire apparat dans la
syntaxe Python sous la forme dune srie dlments spars par des virgules, le tout tant enferm
entre deux accolades. Chacun de ces lments est lui-mme constitu dune paire dobjets : un index et
une valeur, spars par un double point.
Dans un dictionnaire, les index sappellent des cls, et les lments peuvent donc sappeler des paires
cl-valeur. Dans notre dictionnaire dexemple, les cls et les valeurs sont des chanes de caractres.
Veuillez prsent constater que lordre dans lequel les lments apparaissent la dernire ligne ne
correspond pas celui dans lequel nous les avons fournis. Cela na strictement aucune importance :
nous nessaierons jamais dextraire une valeur dun dictionnaire laide dun numro dordre. Au lieu
de cela, nous utiliserons les cls :
>>> print(dico['mouse'])
souris

Remarquez aussi que contrairement ce qui se passe avec les listes, il nest pas ncessaire de faire
appel une mthode particulire (telle que append()) pour ajouter de nouveaux lments un
dictionnaire : il suffit de crer une nouvelle paire cl-valeur.

Oprations sur les dictionnaires


Vous savez dj comment ajouter des lments un dictionnaire. Pour en enlever, vous utiliserez
linstruction intgre del. Crons pour lexemple un autre dictionnaire, destin cette fois contenir
linventaire dun stock de fruits. Les index (ou cls) seront les noms des fruits, et les valeurs seront les
masses de ces fruits rpertories dans le stock (les valeurs sont donc cette fois des donnes de type
numrique).
72 Les

listes et les tuples peuvent eux aussi contenir des dictionnaires, des fonctions, des classes ou des
instances, Nous navions pas mentionn tout cela jusqu ici, afin de ne pas alourdir lexpos.

Les dictionnaires

155

>>> invent = {'pommes': 430, 'bananes': 312, 'oranges' : 274, 'poires' : 137}
>>> print(invent)
{'oranges': 274, 'pommes': 430, 'bananes': 312, 'poires': 137}

Si le patron dcide de liquider toutes les pommes et de ne plus en vendre, nous pouvons enlever cette
entre dans le dictionnaire :
>>> del invent['pommes']
>>> print(invent)
{'oranges': 274, 'bananes': 312, 'poires': 137}

La fonction intgre len() est utilisable avec un dictionnaire : elle en renvoie le nombre dlments :
>>> print(len(invent))
3

Test dappartenance
Dune manire analogue ce qui se passe pour les chanes, les listes et les tuples, linstruction in est
utilisable avec les dictionnaires. Elle permet de savoir si un dictionnaire comprend une cl bien
dtermine73 :
>>> if "pommes" in invent:
...
print("Nous avons des pommes")
... else:
...
print("Pas de pommes. Sorry")
...
Pas de pommes. Sorry

Les dictionnaires sont des objets


On peut appliquer aux dictionnaires un certain nombre de mthodes spcifiques :
La mthode keys() renvoie la squence des cls utilises dans le dictionnaire. Cette squence peut tre
utilise telle quelle dans les expressions, ou convertie en liste ou en tuple si ncessaire, avec les
fonctions intgres correspondantes list() et tuple() :
>>> print(dico.keys())
dict_keys(['computer', 'mouse', 'keyboard'])
>>> for k in dico.keys():
...
print("cl :", k, " --- valeur :", dico[k])
...
cl : computer --- valeur : ordinateur
cl : mouse --- valeur : souris
cl : keyboard --- valeur : clavier
>>> list(dico.keys())
['computer', 'mouse', 'keyboard']
>>> tuple(dico.keys())
('computer', 'mouse', 'keyboard')

De manire analogue, la mthode values() renvoie la squence des valeurs mmorises dans le
dictionnaire :
>>> print(invent.values())
dict_values([274, 312, 137])
73 Dans

les versions de Python antrieures la version 3.0, il fallait faire appel une mthode particulire (la
mthode has_key()) pour effectuer ce test.

156

Approfondir les structures de donnes

Quant la mthode items(), elle extrait du dictionnaire une squence quivalente de tuples. Cette
mthode se rvlera trs utile plus loin, lorsque nous voudrons parcourir un dictionnaire laide
dune boucle :
>>> invent.items()
dict_items([('poires', 137), ('bananes', 312), ('oranges', 274)])
>>> tuple(invent.items())
(('poires', 137), ('bananes', 312), ('oranges', 274))

La mthode copy() permet deffectuer une vraie copie dun dictionnaire. Il faut savoir en effet que la
simple affectation dun dictionnaire existant une nouvelle variable cre seulement une nouvelle
rfrence vers le mme objet, et non un nouvel objet. Nous avons dj discut ce phnomne (aliasing)
propos des listes (voir page 147). Par exemple, linstruction ci-dessous ne dfinit pas un nouveau
dictionnaire (contrairement aux apparences) :
>>> stock = invent
>>> stock
{'oranges': 274, 'bananes': 312, 'poires': 137}

Si nous modifions invent, alors stock est galement modifi, et vice-versa (ces deux noms dsignent
en effet le mme objet dictionnaire dans la mmoire de lordinateur) :
>>> del invent['bananes']
>>> stock
{'oranges': 274, 'poires': 137}

Pour obtenir une vraie copie (indpendante) dun dictionnaire prexistant, il faut employer la
mthode copy() :
>>> magasin = stock.copy()
>>> magasin['prunes'] = 561
>>> magasin
{'oranges': 274, 'prunes': 561, 'poires': 137}
>>> stock
{'oranges': 274, 'poires': 137}
>>> invent
{'oranges': 274, 'poires': 137}

Parcours dun dictionnaire


Vous pouvez utiliser une boucle for pour traiter successivement tous les lments contenus dans un
dictionnaire, mais attention :

au cours de litration, ce sont les cls utilises dans le dictionnaire qui seront successivement
affectes la variable de travail, et non les valeurs ;
lordre dans lequel les lments seront extraits est imprvisible (puisquun dictionnaire nest pas
une squence).
Exemple :
>>> invent ={"oranges":274, "poires":137, "bananes":312}
>>> for clef in invent:
...
print(clef)
...
poires
bananes
oranges

Les dictionnaires

157

Si vous souhaitez effectuer un traitement sur les valeurs, il vous suffit alors de rcuprer chacune
delles partir de la cl correspondante :
>>> for clef in invent:
...
print(clef, invent[clef])
...
poires 137
bananes 312
oranges 274

Cette manire de procder nest cependant pas idale, ni en termes de performances ni mme du point
de vue de la lisibilit. Il est recommand de plutt faire appel la mthode items() dcrite la section
prcdente :
for clef, valeur in invent.items():
print(clef, valeur)
...
poires 137
bananes 312
oranges 274

Dans cet exemple, la mthode items() applique au dictionnaire invent renvoie une squence de
tuples (clef, valeur). Le parcours effectu sur cette liste laide de la boucle for permet dexaminer
chacun de ces tuples un par un.

Les cls ne sont pas ncessairement des chanes de caractres


Jusqu prsent nous avons dcrit des dictionnaires dont les
cls taient chaque fois des valeurs de type string. En fait
nous pouvons utiliser en guise de cls nimporte quel type de
donnes non modifiables : des entiers, des rels, des chanes de
caractres, et mme des tuples.
Considrons par exemple que nous voulions rpertorier les
arbres remarquables situs dans un grand terrain
rectangulaire. Nous pouvons pour cela utiliser un
dictionnaire, dont les cls seront des tuples indiquant les
coordonnes x,y de chaque arbre :
>>>
>>>
>>>
>>>
>>>
>>>

arb = {}
arb[(1,2)]
arb[(3,4)]
arb[6,5] =
arb[5,1] =
arb[7,3] =

= 'Peuplier'
= 'Platane'
'Palmier'
'Cycas'
'Sapin'

>>> print(arb)
{(3, 4): 'Platane', (6, 5): 'Palmier', (5, 1): 'Cycas', (1, 2): 'Peuplier',
(7, 3): 'Sapin'}
>>> print(arb[(6,5)])
palmier

Vous pouvez remarquer que nous avons allg lcriture partir de la troisime ligne, en profitant du
fait que les parenthses dlimitant les tuples sont facultatives ( utiliser avec prudence !).

158

Approfondir les structures de donnes

Dans ce genre de construction, il faut garder lesprit que le dictionnaire contient des lments
seulement pour certains couples de coordonnes. Ailleurs, il ny a rien. Par consquent, si nous
voulons interroger le dictionnaire pour savoir ce qui se trouve l o il ny a rien, comme par exemple
aux coordonnes (2,1), nous allons provoquer une erreur :
>>> print(arb[1,2])
Peuplier
>>> print(arb[2,1])
***** Erreur : KeyError: (2, 1)

*****

Pour rsoudre ce petit problme, nous pouvons utiliser la mthode get() :


>>> arb.get((1,2), 'nant')
Peuplier
>>> arb.get((2,1), 'nant')
nant

Le premier argument transmis cette mthode est la cl de recherche, le second argument est la
valeur que nous voulons obtenir en retour si la cl nexiste pas dans le dictionnaire.

Les dictionnaires ne sont pas des squences


Comme vous lavez vu plus haut, les lments dun dictionnaire ne sont pas disposs dans un ordre
particulier. Des oprations comme la concatnation et lextraction (dun groupe dlments contigus)
ne peuvent donc tout simplement pas sappliquer ici. Si vous essayez tout de mme, Python lvera une
erreur lors de lexcution du code :
>>> print(arb[1:3])
***** Erreur : TypeError: unhashable type *****

Vous avez vu galement quil suffit daffecter un nouvel indice (une nouvelle cl) pour ajouter une
entre au dictionnaire. Cela ne marcherait pas avec les listes74 :
>>> invent['cerises'] = 987
>>> print(invent)
{'oranges': 274, 'cerises': 987, 'poires': 137}
>>> liste =['jambon', 'salade', 'confiture', 'chocolat']
>>> liste[4] ='salami'
***** IndexError: list assignment index out of range

*****

Du fait quils ne sont pas des squences, les dictionnaires se rvlent donc particulirement prcieux
pour grer des ensembles de donnes o lon est amen effectuer frquemment des ajouts ou des
suppressions, dans nimporte quel ordre. Ils remplacent avantageusement les listes lorsquil sagit de
traiter des ensembles de donnes numrotes, dont les numros ne se suivent pas.
Exemple :
>>>
>>>
>>>
>>>

client = {}
client[4317] = "Dupond"
client[256] = "Durand"
client[782] = "Schmidt"
74 Rappel

: les mthodes permettant dajouter des lments une liste sont dcrites page 144.

Les dictionnaires

159

etc.
Exercices
10.45 crivez un script qui cre un mini-systme de base de donnes fonctionnant laide dun
dictionnaire, dans lequel vous mmoriserez les noms dune srie de copains, leur ge et leur
taille. Votre script devra comporter deux fonctions : la premire pour le remplissage du
dictionnaire, et la seconde pour sa consultation. Dans la fonction de remplissage, utilisez une
boucle pour accepter les donnes entres par lutilisateur.
Dans le dictionnaire, le nom de llve servira de cl daccs, et les valeurs seront constitues
de tuples (ge, taille), dans lesquels lge sera exprim en annes (donne de type entier), et la
taille en mtres (donne de type rel).
La fonction de consultation comportera elle aussi une boucle, dans laquelle lutilisateur pourra
fournir un nom quelconque pour obtenir en retour le couple ge, taille correspondant. Le
rsultat de la requte devra tre une ligne de texte bien formate, telle par exemple : Nom :
Jean Dhoute - ge : 15 ans - taille : 1.74 m . Pour obtenir ce rsultat, servez-vous du formatage
des chanes de caractres dcrit la page 138.

10.46 crivez une fonction qui change les cls et les valeurs dun dictionnaire (ce qui permettra par
exemple de transformer un dictionnaire anglais/franais en un dictionnaire franais/anglais).
On suppose que le dictionnaire ne contient pas plusieurs valeurs identiques.

Construction dun histogramme laide dun dictionnaire


Les dictionnaires constituent un outil trs lgant pour construire des histogrammes.
Supposons par exemple que nous voulions tablir lhistogramme qui reprsente la frquence dutilisation de chacune des lettres de lalphabet dans un texte donn. Lalgorithme permettant de raliser ce
travail est extraordinairement simple si on le construit sur base dun dictionnaire :
>>> texte ="les saucisses et saucissons secs sont dans le saloir"
>>> lettres ={}
>>> for c in texte:
...
lettres[c] =lettres.get(c, 0) + 1
...
>>> print(lettres)
{'t': 2, 'u': 2, 'r': 1, 's': 14, 'n': 3, 'o': 3, 'l': 3, 'i': 3, 'd': 1, 'e': 5, 'c':
3, ' ': 8, 'a': 4}

Nous commenons par crer un dictionnaire vide : lettres. Ensuite, nous allons remplir ce dictionnaire
en utilisant les caractres de lalphabet en guise de cls. Les valeurs que nous mmoriserons pour
chacune de ces cls seront les frquences des caractres correspondants dans le texte. Afin de calculer
celles-ci, nous effectuons un parcours de la chane de caractres texte. Pour chacun de ces caractres,
nous interrogeons le dictionnaire laide de la mthode get(), en utilisant le caractre en guise de cl,
afin dy lire la frquence dj mmorise pour ce caractre. Si cette valeur nexiste pas encore, la
mthode get() doit renvoyer une valeur nulle. Dans tous les cas, nous incrmentons la valeur trouve,
et nous la mmorisons dans le dictionnaire, lemplacement qui correspond la cl (cest--dire au
caractre en cours de traitement).
Pour fignoler notre travail, nous pouvons encore souhaiter afficher lhistogramme dans lordre
alphabtique. Pour ce faire, nous pensons immdiatement la mthode sort(), mais celle-ci ne peut

160

Approfondir les structures de donnes

sappliquer quaux listes. Qu cela ne tienne ! Nous avons vu plus haut comment nous pouvions
convertir un dictionnaire en une liste de tuples :
>>> lettres_triees = list(lettres.items())
>>> lettres_triees.sort()
>>> print(lettres_triees)
[(' ', 8), ('a', 4), ('c', 3), ('d', 1), ('e', 5), ('i', 3), ('l', 3), ('n', 3), ('o', 3
), ('r', 1), ('s', 14), ('t', 2), ('u', 2)]

Exercices
10.47 Vous avez votre disposition un fichier texte quelconque (pas trop gros). crivez un script qui
compte les occurrences de chacune des lettres de lalphabet dans ce texte (on simplifiera le
problme en ne tenant pas compte des lettres accentues).

10.48 Modifiez le script ci-dessus afin quil tablisse une table des occurrences de chaque mot dans le
texte. Conseil : dans un texte quelconque, les mots ne sont pas seulement spars par des
espaces, mais galement par divers signes de ponctuation. Pour simplifier le problme, vous
pouvez commencer par remplacer tous les caractres non-alphabtiques par des espaces, et
convertir la chane rsultante en une liste de mots laide de la mthode split().
10.49 Vous avez votre disposition un fichier texte quelconque (pas trop gros). crivez un script qui
analyse ce texte, et mmorise dans un dictionnaire lemplacement exact de chacun des mots
(compt en nombre de caractres partir du dbut). Lorsquun mme mot apparat plusieurs
fois, tous ses emplacements doivent tre mmoriss : chaque valeur de votre dictionnaire doit
donc tre une liste demplacements.

Contrle du flux dexcution laide dun dictionnaire


Il arrive frquemment que lon ait diriger lexcution dun programme dans diffrentes directions,
en fonction de la valeur prise par une variable. Vous pouvez bien videmment traiter ce problme
laide dune srie dinstructions if - elif - else , mais cela peut devenir assez lourd et inlgant si vous
avez affaire un grand nombre de possibilits. Exemple :
materiau = input("Choisissez le matriau : ")
if materiau == 'fer':
fonctionA()
elif materiau == 'bois':
fonctionC()
elif materiau == 'cuivre':
fonctionB()
elif materiau == 'pierre':
fonctionD()
elif
... etc ...

Les langages de programmation proposent souvent des instructions spcifiques pour traiter ce genre
de problme, telles les instructions switch ou case du C ou du Pascal. Python nen propose aucune, mais
vous pouvez vous tirer daffaire dans bien des cas laide dune liste (nous en donnons un exemple
dtaill la page 251), ou mieux encore laide dun dictionnaire. Exemple :
materiau = input("Choisissez le matriau : ")
dico = {'fer':fonctionA,

Les dictionnaires

161

'bois':fonctionC,
'cuivre':fonctionB,
'pierre':fonctionD,
... etc ...}
dico[materiau]()

Les deux instructions ci-dessus pourraient tre condenses en une seule, mais nous les laissons
spares pour bien dtailler le mcanisme :

La premire instruction dfinit un dictionnaire dico dans lequel les cls sont les diffrentes
possibilits pour la variable materiau, et les valeurs, les noms des fonctions invoquer en
correspondance. Notez bien quil sagit seulement des noms de ces fonctions, quil ne faut surtout
pas faire suivre de parenthses dans ce cas (sinon Python excuterait chacune de ces fonctions au
moment de la cration du dictionnaire).
La seconde instruction invoque la fonction correspondant au choix opr laide de la variable
materiau. Le nom de la fonction est extrait du dictionnaire laide de la cl, puis associ une
paire de parenthses. Python reconnat alors un appel de fonction tout fait classique, et lexcute.
Vous pouvez encore amliorer la technique ci-dessus en remplaant cette instruction par sa variante
ci-dessous, qui fait appel la mthode get() afin de prvoir le cas o la cl demande nexisterait pas
dans le dictionnaire (vous obtenez de cette faon lquivalent dune instruction else terminant une
longue srie de elif) :
dico.get(materiau, fonctAutre)()

Lorsque la la valeur de la variable materiau ne correspond aucune cl du dictionnaire, cest la


fonction fonctAutre() qui est invoque.
Exercices
10.50 Compltez lexercice 10.46 (mini-systme de base de donnes) en lui ajoutant deux fonctions :
lune pour enregistrer le dictionnaire rsultant dans un fichier texte, et lautre pour
reconstituer ce dictionnaire partir du fichier correspondant.
Chaque ligne de votre fichier texte correspondra un lment du dictionnaire. Elle sera
formate de manire bien sparer :
- la cl et la valeur (cest--dire le nom de la personne, dune part, et lensemble : ge +
taille , dautre part ;
- dans lensemble ge + taille , ces deux donnes numriques.
Vous utiliserez donc deux caractres sparateurs diffrents, par exemple @ pour sparer la
cl et la valeur, et # pour sparer les donnes constituant cette valeur :
Juliette@18#1.67
Jean-Pierre@17#1.78
Delphine@19#1.71
Anne-Marie@17#1.63 etc.

10.51 Amliorez encore le script de lexercice prcdent, en utilisant un dictionnaire pour diriger le
flux dexcution du programme au niveau du menu principal.
Votre programme affichera par exemple :
Choisissez :

162

Approfondir les structures de donnes


(R)cuprer un dictionnaire prexistant sauvegard dans un fichier
(A)jouter des donnes au dictionnaire courant
(C)onsulter le dictionnaire courant
(S)auvegarder le dictionnaire courant dans un fichier
(T)erminer :

Suivant le choix opr par lutilisateur, vous effectuerez alors lappel de la fonction
correspondante en la slectionnant dans un dictionnaire de fonctions.

11
Classes, objets, attributs

11

Les chapitres prcdents vous ont dj mis en contact plusieurs reprises avec la notion dobjet.
Vous savez donc dj quun objet est une entit que lon construit par instanciation partir dune
classe (cest--dire en quelque sorte une catgorie ou un type dobjet). Par exemple, on peut
trouver dans la bibliothque Tkinter, une classe Button() partir de laquelle on peut crer dans
une fentre un nombre quelconque de boutons.
Nous allons prsent examiner comment vous pouvez vous-mmes dfinir de nouvelles classes
dobjets. Il sagit l dun sujet relativement ardu, mais vous laborderez de manire trs progressive,
en commenant par dfinir des classes dobjets trs simples, que vous perfectionnerez ensuite.
Comme les objets de la vie courante, les objets informatiques peuvent tre trs simples ou trs
compliqus. Ils peuvent tre composs de diffrentes parties, qui soient elles-mmes des objets,
ceux-ci tant faits leur tour dautres objets plus simples, etc.

Utilit des classes


Les classes sont les principaux outils de la programmation oriente objet (Object Oriented Programming
ou OOP). Ce type de programmation permet de structurer les logiciels complexes en les organisant
comme des ensembles dobjets qui interagissent, entre eux et avec le monde extrieur.
Le premier bnfice de cette approche de la programmation rside dans le fait que les diffrents objets
utiliss peuvent tre construits indpendamment les uns des autres (par exemple par des
programmeurs diffrents) sans quil ny ait de risque dinterfrence. Ce rsultat est obtenu grce au
concept dencapsulation : la fonctionnalit interne de lobjet et les variables quil utilise pour effectuer
son travail, sont en quelque sorte enfermes dans lobjet. Les autres objets et le monde extrieur ne
peuvent y avoir accs qu travers des procdures bien dfinies : linterface de lobjet.
En particulier, lutilisation de classes dans vos programmes va vous permettre entre autres avantages
dviter au maximum lemploi de variables globales. Vous devez savoir en effet que lutilisation de variables
globales comporte des risques, dautant plus importants que les programmes sont volumineux, parce
quil est toujours possible que de telles variables soient modifies, ou mme redfinies, nimporte o
dans le corps du programme (ce risque saggrave particulirement si plusieurs programmeurs
diffrents travaillent sur un mme logiciel).

164

Classes, objets, attributs

Un second bnfice rsultant de lutilisation des classes est la possibilit quelles offrent de construire
de nouveaux objets partir dobjets prexistants, et donc de rutiliser des pans entiers dune
programmation dj crite (sans toucher celle-ci !), pour en tirer une fonctionnalit nouvelle. Cela
est rendu possible grce aux concepts de drivation et de polymorphisme :

La drivation est le mcanisme qui permet de construire une classe enfant au dpart dune
classe parente . Lenfant ainsi obtenu hrite toutes les proprits et toute la fonctionnalit de
son anctre, auxquelles on peut ajouter ce que lon veut.
Le polymorphisme permet dattribuer des comportements diffrents des objets drivant les uns
des autres, ou au mme objet ou en fonction dun certain contexte.
Avant daller plus loin, signalons ici que la programmation oriente objet est optionnelle sous Python.
Vous pouvez donc mener bien de nombreux projets sans lutiliser, avec des outils plus simples tels
que les fonctions. Sachez cependant que si vous faites leffort dapprendre programmer laide de
classes, vous matriserez un niveau dabstraction plus lev, ce qui vous permettra de traiter des
problmes de plus en plus complexes. En dautres termes, vous deviendrez un programmeur beaucoup
plus comptent. Pour vous en convaincre, rappelez-vous les progrs que vous avez dj raliss au
long de ce cours :
Au dbut de votre tude, vous avez dabord utilis de simples instructions. Vous avez en quelque
sorte programm la main (cest--dire pratiquement sans outils).
Lorsque vous avez dcouvert les fonctions prdfinies (cf. chapitre 6), vous avez appris quil
existait ainsi de vastes collections doutils spcialiss, raliss par dautres programmeurs.
En apprenant crire vos propres fonctions (cf. chapitre 7 et suivants), vous tes devenu capable
de crer vous-mme de nouveaux outils, ce qui vous a donn un surcrot de puissance
considrable.
Si vous vous initiez maintenant la programmation par classes, vous allez apprendre construire
des machines productrices doutils. Cest videmment plus complexe que de fabriquer directement
ces outils, mais cela vous ouvre des perspectives encore bien plus larges !
Une bonne comprhension des classes vous aidera notamment bien matriser le domaine des
interfaces graphiques (tkinter, wxPython) et vous prparera efficacement aborder dautres langages
modernes, tels que C++ ou Java.

Dfinition dune classe lmentaire


Pour crer une nouvelle classe dobjets Python, on utilise linstruction class. Nous allons donc
apprendre utiliser cette instruction, en commenant par dfinir un type dobjet trs rudimentaire,
lequel sera simplement un nouveau type de donne. Nous avons dj utilis diffrents types de
donnes jusqu prsent, mais il sagissait chaque fois de types intgrs dans le langage lui-mme.
Nous allons maintenant crer un nouveau type composite : le type Point.
Ce type correspondra au concept de point en gomtrie plane. Dans un plan, un point est caractris
par deux nombres (ses coordonnes suivant x et y). En notation mathmatique, on reprsente donc un
point par ses deux coordonnes x et y enfermes dans une paire de parenthses. On parlera par
exemple du point (25, 17). Une manire naturelle de reprsenter un point sous Python serait dutiliser

Dfinition dune classe lmentaire

165

pour les coordonnes deux valeurs de type float. Nous voudrions cependant combiner ces deux valeurs
dans une seule entit, ou un seul objet. Pour y arriver, nous allons dfinir une classe Point() :
>>> class Point(object):
...
"Dfinition d'un point gomtrique"

Les dfinitions de classes peuvent tre situes nimporte o dans un programme, mais on les placera
en gnral au dbut (ou bien dans un module importer). Lexemple ci-dessus est probablement le
plus simple qui se puisse concevoir. Une seule ligne nous a suffi pour dfinir le nouveau type dobjet
Point().
Remarquons demble que :

Linstruction class est un nouvel exemple dinstruction compose. Noubliez pas le double point
obligatoire la fin de la ligne, et lindentation du bloc dinstructions qui suit. Ce bloc doit contenir
au moins une ligne. Dans notre exemple ultra-simplifi, cette ligne nest rien dautre quun simple
commentaire. Comme nous lavons vu prcdemment pour les fonctions (cf. page 71), vous pouvez
insrer une chane de caractres directement aprs linstruction class, afin de mettre en place un
commentaire qui sera automatiquement incorpor dans le dispositif de documentation interne de
Python. Prenez donc lhabitude de toujours placer une chane dcrivant la classe cet endroit.
Les parenthses sont destines contenir la rfrence dune classe prexistante. Cela est requis
pour permettre le mcanisme dhritage. Toute classe nouvelle que nous crons peut en effet
hriter dune classe parente un ensemble de caractristiques, auxquelles elle ajoutera les siennes
propres. Lorsque lon dsire crer une classe fondamentale cest--dire ne drivant daucune
autre, comme cest le cas ici avec notre classe Point() la rfrence indiquer doit tre par
convention le nom spcial object, lequel dsigne lanctre de toutes les classes75.
Une convention trs rpandue veut que lon donne aux classes des noms qui commencent par une
majuscule. Dans la suite de ce texte, nous respecterons cette convention, ainsi quune autre qui
demande que dans les textes explicatifs, on associe chaque nom de classe une paire de
parenthses, comme nous le faisons dj pour les noms de fonctions.
Nous venons donc de dfinir une classe Point(). Nous pouvons prsent nous en servir pour crer des
objets de cette classe, que lon appellera aussi des instances de cette classe. Lopration sappelle pour
cette raison une instanciation. Crons par exemple un nouvel objet p976 :
>>> p9 = Point()

Aprs cette instruction, la variable p9 contient la rfrence dun nouvel objet Point(). Nous pouvons
dire galement que p9 est une nouvelle instance de la classe Point().

75 Lorsque

vous dfinissez une classe fondamentale, vous pouvez omettre les parenthses et la rfrence la
classe anctre object : ces indications sont devenues facultatives sous Python 3. Nous continuerons
cependant les utiliser dans la suite de ce texte, afin de bien marquer limportance du concept dhritage.
76 Sous Python, on peut donc instancier un objet l aide dune simple instruction daffectation. Dautres
langages imposent lemploi dune instruction spciale, souvent appele new pour bien montrer que lon cre
un nouvel objet partir dun moule. Exemple : p9 = new Point().

166

Classes, objets, attributs


Attention
Comme les fonctions, les classes auxquelles on fait appel dans une instruction doivent
toujours tre accompagnes de parenthses (mme si aucun argument nest transmis).
Nous verrons un peu plus loin que les classes peuvent effectivement tre appeles avec
des arguments.

Voyons maintenant si nous pouvons faire quelque chose avec notre nouvel objet p9 :
>>> print(p9)
<__main__.Point object at 0xb76f132c>

Le message renvoy par Python indique, comme vous laurez certainement bien compris tout de suite,
que p9 est une instance de la classe Point(), laquelle est dfinie elle-mme au niveau principal (main)
du programme. Elle est situe dans un emplacement bien dtermin de la mmoire vive, dont ladresse
apparat ici en notation hexadcimale.
>>> print(p9.__doc__)
Dfinition d'un point gomtrique

Comme nous lavons expliqu pour les fonctions (cf. page 71), les chanes de documentation de divers
objets Python sont associes lattribut prdfini __doc__. Il est donc toujours possible de retrouver
la documentation associe un objet Python quelconque, en invoquant cet attribut.

Attributs (ou variables) dinstance


Lobjet que nous venons de crer est juste une coquille vide. Nous allons prsent lui ajouter des
composants, par simple assignation, en utilisant le systme de qualification des noms par points 77 :
>>> p9.x = 3.0
>>> p9.y = 4.0

Les variables x et y que nous avons ainsi dfinies en les liant demble
p9 , sont dsormais des attributs de lobjet p9. On peut galement les
appeler des variables d'instance. Elles sont en effet incorpores, ou plutt
encapsules dans cette instance (ou objet). Le diagramme dtat ci-contre
montre le rsultat de ces affectations : la variable p9 contient la
rfrence indiquant lemplacement mmoire du nouvel objet, qui
contient lui-mme les deux attributs x et y. Ceux-ci contiennent les rfrences des valeurs 3.0 et 4.0,
mmorises ailleurs.
On pourra utiliser les attributs dun objet dans nimporte quelle expression, exactement comme toutes
les variables ordinaires :
>>> print(p9.x)
3.0
77 Ce

systme de notation est similaire celui que nous utilisons pour dsigner les variables d un module,
comme math.pi ou string.ascii_lowercase. Nous aurons loccasion dy revenir plus tard, mais sachez ds
prsent que les modules peuvent contenir des fonctions, mais aussi des classes et des variables. Essayez par
exemple :
>>>
>>>
>>>
>>>
>>>

import string
string.capwords
string.ascii_uppercase
string.punctuation
string.hexdigits

Attributs (ou variables) dinstance

167

>>> print(p9.x**2 + p9.y**2)


25.0

Du fait de leur encapsulation dans lobjet, les attributs sont des variables distinctes dautres variables
qui pourraient porter le mme nom. Par exemple, linstruction x = p9.x signifie : extraire de lobjet
rfrenc par p9 la valeur de son attribut x, et assigner cette valeur la variable x . Il ny a pas de
conflit entre la variable indpendante x , et lattribut x de lobjet p9. Lobjet p9 contient en effet son
propre espace de noms, indpendant de lespace de nom principal o se trouve la variable x.
Important : les exemples donns ici sont provisoires.
Nous venons de voir quil est trs ais dajouter un attribut un objet en utilisant une
simple instruction dassignation telle que p9.x = 3.0 On peut se permettre cela sous
Python (cest une consquence de son caractre foncirement dynamique), mais cela
nest pas vraiment recommandable, comme vous le comprendrez plus loin. Nous
nutiliserons donc cette faon de faire que de manire anecdotique, et uniquement dans
le but de simplifier nos premires explications concernant les attributs dinstances. La
bonne manire de procder sera dveloppe dans le chapitre suivant.

Passage dobjets comme arguments dans lappel dune fonction


Les fonctions peuvent utiliser des objets comme paramtres, et elles peuvent galement fournir un
objet comme valeur de retour. Par exemple, vous pouvez dfinir une fonction telle que celle-ci :
>>> def affiche_point(p):
...
print("coord. horizontale =", p.x, "coord. verticale =", p.y)

Le paramtre p utilis par cette fonction doit tre un objet de type Point(), dont linstruction qui suit
utilisera les variables dinstance p.x et p.y. Lorsquon appelle cette fonction, il faut donc lui fournir un
objet de type Point() comme argument. Essayons avec lobjet p9 :
>>> affiche_point(p9)
coord. horizontale = 3.0 coord. Verticale = 4.0

Exercice
11.1 crivez une fonction distance() qui permette de calculer la distance entre deux points. (Il
faudra vous rappeler le thorme de Pythagore !)
Cette fonction attendra videmment deux objets Point() comme arguments.

Similitude et unicit
Dans la langue parle, les mmes mots peuvent avoir des significations fort diffrentes suivant le
contexte dans lequel on les utilise. La consquence en est que certaines expressions utilisant ces mots
peuvent tre comprises de plusieurs manires diffrentes (expressions ambigus).
Le mot mme , par exemple, a des significations diffrentes dans les phrases : Charles et moi
avons la mme voiture et Charles et moi avons la mme mre . Dans la premire, ce que je veux
dire est que la voiture de Charles et la mienne sont du mme modle. Il sagit pourtant de deux
voitures distinctes. Dans la seconde, jindique que la mre de Charles et la mienne constituent en fait
une seule et unique personne.

168

Classes, objets, attributs

Lorsque nous traitons dobjets logiciels, nous pouvons rencontrer la mme ambigut. Par exemple, si
nous parlons de lgalit de deux objets Point(), cela signifie-t-il que ces deux objets contiennent les
mmes donnes (leurs attributs), ou bien cela signifie-t-il que nous parlons de deux rfrences un
mme et unique objet ? Considrez par exemple les instructions suivantes :
>>> p1 = Point()
>>> p1.x = 3
>>> p1.y = 4
>>> p2 = Point()
>>> p2.x = 3
>>> p2.y = 4
>>> print(p1 == p2)
False

Ces instructions crent deux objets p1 et p2 qui restent distincts, mme sils font partie dune mme
classe et ont des contenus similaires. La dernire instruction teste lgalit de ces deux objets (double
signe gale), et le rsultat est False (faux) : il ny a donc pas galit.
On peut confirmer cela dune autre manire encore :
>>> print(p1)
<__main__.Point instance at 00C2CBEC>
>>> print(p2)
<__main__.Point instance at 00C50F9C>

Linformation est claire : les deux variables p1 et p2 rfrencent bien des objets diffrents, mmoriss
des emplacements diffrents dans la mmoire de lordinateur.
Essayons autre chose, prsent :
>>> p2 = p1
>>> print(p1 == p2)
True

Par linstruction p2 = p1 , nous assignons le contenu de p1 p2. Cela signifie que dsormais ces deux
variables rfrencent le mme objet. Les variables p1 et p2 sont des alias78 lune de lautre.
Le test dgalit dans linstruction suivante renvoie cette fois la valeur True, ce qui signifie que lexpression entre parenthses est vraie : p1 et p2 dsignent bien toutes deux un seul et unique objet,
comme on peut sen convaincre en essayant encore :
>>> p1.x = 7
>>> print(p2.x)
7

Lorsquon modifie lattribut x de p1, on constate que lattribut x de p2 a chang, lui aussi.
>>> print(p1)
<__main__.Point instance at 00C2CBEC>
>>> print(p2)
<__main__.Point instance at 00C2CBEC>

Les deux rfrences p1 et p2 pointent vers le mme emplacement dans la mmoire.

78 Concernant ce phnomne daliasing, voir galement page

147.

Objets composs dobjets

169

Objets composs dobjets


Supposons maintenant que nous voulions dfinir une classe qui servira reprsenter des rectangles.
Pour simplifier, nous allons considrer que ces rectangles seront toujours orients horizontalement ou
verticalement, et jamais en oblique.
De quelles informations avons-nous besoin pour dfinir de tels rectangles ?
Il existe plusieurs possibilits. Nous pourrions par exemple spcifier la position du centre du rectangle
(deux coordonnes) et prciser sa taille (largeur et hauteur). Nous pourrions aussi spcifier les
positions du coin suprieur gauche et du coin infrieur droit. Ou encore la position du coin suprieur
gauche et la taille. Admettons ce soit cette dernire convention qui soit retenue.
Dfinissons donc notre nouvelle classe :
>>> class Rectangle(object):
"dfinition d'une classe de rectangles"

... et servons nous-en tout de suite pour crer une instance :


>>> boite = Rectangle()
>>> boite.largeur = 50.0
>>> boite.hauteur = 35.0

Nous crons ainsi un nouvel objet Rectangle() et lui donnons ensuite deux attributs. Pour spcifier le
coin suprieur gauche, nous allons prsent utiliser une nouvelle instance de la classe Point() que
nous avons dfinie prcdemment. Ainsi nous allons crer un objet, lintrieur dun autre objet !
>>> boite.coin = Point()
>>> boite.coin.x = 12.0
>>> boite.coin.y = 27.0

la premire de ces trois instructions, nous crons un nouvel attribut coin pour lobjet boite. Ensuite,
pour accder cet objet qui se trouve lui-mme lintrieur dun autre objet, nous utilisons la qualification des noms hirarchise ( laide de points) que nous avons dj rencontre plusieurs reprises.
Ainsi lexpression boite.coin.y signifie Aller lobjet rfrenc dans la variable boite. Dans cet
objet, reprer lattribut coin, puis aller lobjet rfrenc dans cet attribut. Une fois cet autre objet
trouv, slectionner son attribut y.
Vous pourrez peut-tre mieux vous reprsenter tout cela laide dun diagramme tel que celui-ci :

170

Classes, objets, attributs

Le nom boite se trouve dans lespace de noms principal. Il rfrence un autre espace de noms rserv
lobjet correspondant, dans lequel sont mmoriss les noms largeur, hauteur et coin. Ceux-ci
rfrencent leur tour, soit dautres espaces de noms (cas du nom coin ), soit des valeurs bien
dtermines, lesquelles sont mmorises ailleurs.
Python rserve des espaces de noms diffrents pour chaque module, chaque classe, chaque instance,
chaque fonction. Vous pouvez tirer parti de tous ces espaces de noms bien compartiments afin de
raliser des programmes robustes, cest--dire des programmes dont les diffrents composants ne
peuvent pas facilement interfrer.

Objets comme valeurs de retour dune fonction


Nous avons vu plus haut que les fonctions peuvent utiliser des objets comme paramtres. Elles
peuvent galement transmettre une instance comme valeur de retour. Par exemple, la fonction trouveCentre() ci-dessous doit tre appele avec un argument de type Rectangle() et elle renvoie un objet
de type Point(), lequel contiendra les coordonnes du centre du rectangle.
>>> def trouveCentre(box):
...
p = Point()
...
p.x = box.coin.x + box.largeur/2.0
...
p.y = box.coin.y + box.hauteur/2.0
...
return p

Vous pouvez par exemple appeler cette fonction, en utilisant comme argument lobjet boite dfini
plus haut :
>>> centre = trouveCentre(boite)
>>> print(centre.x, centre.y)
37.0 44.5

Modification des objets


Nous pouvons changer les proprits dun objet en assignant de nouvelles valeurs ses attributs. Par
exemple, nous pouvons modifier la taille dun rectangle (sans modifier sa position), en rassignant ses
attributs hauteur et largeur :
>>> boite.hauteur = boite.hauteur + 20
>>> boite.largeur = boite.largeur 5

Nous pouvons faire cela sous Python, parce que dans ce langage les proprits des objets sont toujours
publiques (du moins jusqu la version actuelle 3.1). Dautres langages tablissent une distinction nette
entre attributs publics (accessibles de lextrieur de lobjet) et attributs privs (qui sont accessibles
seulement aux algorithmes inclus dans lobjet lui-mme).
Cependant, comme nous lavons dj signal plus haut ( propos de la dfinition des attributs par
assignation simple, depuis lextrieur de lobjet), modifier de cette faon les attributs dune instance
nest pas une pratique recommandable, parce quelle contredit lun des objectifs fondamentaux de
la programmation oriente objet, qui vise tablir une sparation stricte entre la fonctionnalit dun
objet (telle quelle a t dclare au monde extrieur) et la manire dont cette fonctionnalit est
rellement implmente dans lobjet (et que le monde extrieur na pas connatre).

Modification des objets

171

Concrtement, cela signifie que nous devons maintenant tudier comment faire fonctionner les objets
laide doutils vraiment appropris, que nous appellerons des mthodes.
Ensuite, lorsque nous aurons bien compris le maniement de celles-ci, nous nous fixerons pour rgle de ne
plus modifier les attributs dun objet par assignation directe depuis le monde extrieur, comme nous lavons
fait jusqu prsent. Nous veillerons au contraire toujours utiliser pour cela des mthodes mises en
place spcifiquement dans ce but, comme nous allons lexpliquer dans le chapitre suivant. Lensemble
de ces mthodes constituera ce que nous appellerons dsormais linterface de lobjet.

12
Classes, mthodes, hritage

12

Les classes que nous avons dfinies dans le chapitre prcdent peuvent tre considres comme des
espaces de noms particuliers, dans lesquels nous navons plac jusquici que des variables (les
attributs dinstance). Il nous faut prsent doter ces classes dune fonctionnalit.

Lide de base de la programmation oriente objet consiste en effet regrouper dans un mme
ensemble (lobjet), la fois un certain nombre de donnes (ce sont les attributs dinstance), et les
algorithmes destins effectuer divers traitements sur ces donnes (ce sont les mthodes, savoir des
fonctions particulires encapsules dans lobjet).
Objet = [ attributs + mthodes ]
Cette faon dassocier dans une mme capsule les proprits dun objet et les fonctions qui
permettent dagir sur elles, correspond chez les concepteurs de programmes une volont de
construire des entits informatiques dont le comportement se rapproche du comportement des objets
du monde rel qui nous entoure.
Considrons par exemple un widget bouton dans une application graphique. Il nous parat
raisonnable de souhaiter que lobjet informatique que nous appelons ainsi ait un comportement qui
ressemble celui dun bouton dappareil quelconque dans le monde rel. Or nous savons que la
fonctionnalit dun bouton rel (sa capacit de fermer ou douvrir un circuit lectrique) est bien
intgre dans lobjet lui-mme (au mme titre que dautres proprits, telles que sa taille, sa couleur,
etc.). De la mme manire, nous souhaiterons donc que les diffrentes caractristiques de notre
bouton logiciel (sa taille, son emplacement, sa couleur, le texte quil supporte), mais aussi la dfinition
de ce qui se passe lorsque lon effectue diffrentes actions de la souris sur ce bouton, soient regroups
dans une entit bien prcise lintrieur du programme, de telle sorte quil ny ait pas de confusion
entre ce bouton et un autre, ou a fortiori entre ce bouton et dautres entits.

174

Classes, mthodes, hritage

Dfinition dune mthode


Pour illustrer notre propos, nous allons dfinir une nouvelle classe Time(), laquelle devrait nous
permettre deffectuer toute une srie doprations sur des instants, des dures, etc. :
>>> class Time(object):
...
"dfinition d'objets temporels"

Crons prsent un objet de ce type, et ajoutons-lui des variables dinstance pour mmoriser les
heures, minutes et secondes :
>>>
>>>
>>>
>>>

instant = Time()
instant.heure = 11
instant.minute = 34
instant.seconde = 25

titre dexercice, crivez maintenant vous-mme une fonction affiche_heure() , qui serve visualiser
le contenu dun objet de classe Time() sous la forme conventionnelle heures:minutes:secondes .
Applique lobjet instant cr ci-dessus, cette fonction devrait donc afficher 11:34:25 :
>>> affiche_heure(instant)
11:34:25

Votre fonction ressemblera probablement ceci :


>>> def affiche_heure(t):
...
print(str(t.heure) +":" +str(t.minute) +":" +str(t.seconde))

... ou mieux encore, ceci :


>>> def affiche_heure(t):
...
print("{0}:{1}:{2}".format(t.heure, t.minute, t.seconde))

en application de la technique de formatage des chanes dcrite la page 138.


Si par la suite vous deviez utiliser frquemment des objets de la classe Time(), cette fonction daffichage vous serait probablement fort utile.
Il serait donc judicieux darriver encapsuler cette fonction affiche_heure() dans la classe Time()
elle-mme, de manire sassurer quelle soit toujours automatiquement disponible, chaque fois que
lon aura manipuler des objets de la classe Time().
Une fonction que lon aura ainsi encapsule dans une classe sappelle prfrentiellement une mthode.
Vous avez videmment dj rencontr des mthodes de nombreuses reprises dans les chapitres
prcdents de cet ouvrage, et vous savez donc dj quune mthode est bien une fonction associe
une classe particulire dobjets. Il vous reste seulement apprendre comment construire une telle
fonction.

Dfinition dune mthode

175

Dfinition concrte dune mthode dans un script


On dfinit une mthode comme on dfinit une fonction, cest--dire en crivant un bloc dinstructions
la suite du mot rserv def, mais cependant avec deux diffrences :

la dfinition dune mthode est toujours place lintrieur de la dfinition dune classe , de manire
ce que la relation qui lie la mthode la classe soit clairement tablie ;
la dfinition dune mthode doit toujours comporter au moins un paramtre, lequel doit tre une
rfrence dinstance, et ce paramtre particulier doit toujours tre list en premier.
Vous pourriez en principe utiliser un nom de variable quelconque pour ce premier paramtre, mais il
est vivement conseill de respecter la convention qui consiste toujours lui donner le nom : self.
Ce paramtre self est ncessaire, parce quil faut pouvoir dsigner linstance laquelle la mthode sera
associe, dans les instructions faisant partie de sa dfinition. Vous comprendrez cela plus facilement
avec les exemples ci-aprs.
Remarquons que la dfinition dune mthode comporte toujours au moins un paramtre :
self, alors que la dfinition dune fonction peut nen comporter aucun.

Voyons comment cela se passe en pratique :


Pour faire en sorte que la fonction affiche_heure() devienne une mthode de la classe Time(), il nous
suffit de dplacer sa dfinition lintrieur de celle de la classe :
>>> class Time(object):
...
"Nouvelle classe temporelle"
...
def affiche_heure(t):
...
print("{0}:{1}:{2}".format(t.heure, t.minute, t.seconde))

Techniquement, cest tout fait suffisant, car le paramtre t peut parfaitement dsigner linstance
laquelle seront attachs les attributs heure, minute et seconde. tant donn son rle particulier, il est
cependant fortement recommand de changer son nom en self :
>>> class Time(object):
...
"Nouvelle classe temporelle"
...
def affiche_heure(self):
...
print("{0}:{1}:{2}".format(self.heure, self.minute, self.seconde))

La dfinition de la mthode affiche_heure() fait maintenant partie du bloc dinstructions indentes


suivant linstruction class (et dont fait partie aussi la chane documentaire Nouvelle classe
temporelle ).

Essai de la mthode, dans une instance quelconque


Nous disposons donc ds prsent dune classe Time(), dote dune mthode affiche_heure(). En
principe, nous devons maintenant pouvoir crer des objets de cette classe, et leur appliquer cette
mthode. Voyons si cela fonctionne. Pour ce faire, commenons par instancier un objet :
>>> maintenant = Time()

Si nous essayons un peu trop vite de tester notre nouvelle mthode sur cet objet, cela ne marche pas :

176

Classes, mthodes, hritage

>>> maintenant.affiche_heure()
AttributeError: 'Time' object has no attribute 'heure'

Cest normal : nous navons pas encore cr les attributs dinstance. Il faudrait faire par exemple :
>>> maintenant.heure = 13
>>> maintenant.minute = 34
>>> maintenant.seconde = 21

... et ressayer. prsent, a marche :


>>> maintenant.affiche_heure()
13:34:21

plusieurs reprises, nous avons cependant dj signal quil nest pas recommandable de crer ainsi
des attributs dinstance par assignation directe en dehors de lobjet lui-mme. Entre autres
dsagrments, cela conduirait frquemment des erreurs comme celle que nous venons de
rencontrer. Voyons donc prsent comment nous pouvons mieux faire.

La mthode constructeur
Lerreur que nous avons rencontre au paragraphe prcdent est-elle vitable ?
Elle ne se produirait effectivement pas, si nous nous tions arrangs pour que la mthode
affiche_heure() puisse toujours afficher quelque chose, sans quil ne soit ncessaire deffectuer au
pralable une manipulation sur lobjet nouvellement cr. En dautres termes, il serait judicieux que
les variables dinstance soient prdfinies elles aussi lintrieur de la classe, avec pour chacune delles
une valeur par dfaut .
Pour obtenir cela, nous allons faire appel une mthode particulire, que lon dsignera par la suite
sous le nom de constructeur. Une mthode constructeur a ceci de particulier quelle est excute
automatiquement lorsque lon instancie un nouvel objet partir de la classe. On peut donc y placer tout
ce qui semble ncessaire pour initialiser automatiquement lobjet que lon cre.
Afin quelle soit reconnue comme telle par Python, la mthode constructeur devra obligatoirement
sappeler __init__ (deux caractres soulign , le mot init, puis encore deux caractres soulign ).

Exemple
>>> class Time(object):
...
"Encore une nouvelle classe temporelle"
...
def __init__(self):
...
self.heure =12
...
self.minute =0
...
self.seconde =0
...
def affiche_heure(self):
...
print("{0}:{1}:{2}".format(self.heure, self.minute, self.seconde))

Comme prcdemment, crons un objet de cette classe et testons-en la mthode affiche_heure() :

La mthode constructeur

177

>>> tstart = Time()


>>> tstart.affiche_heure()
12:0:0

Nous nobtenons plus aucune erreur, cette fois. En effet : lors de son instanciation, lobjet tstart sest
vu attribuer automatiquement les trois attributs heure, minute et seconde par la mthode
constructeur, avec 12 et zro comme valeurs par dfaut. Ds lors quun objet de cette classe existe, on
peut donc tout de suite demander laffichage de ces attributs.
Lintrt de cette technique apparatra plus clairement si nous ajoutons encore quelque chose.
Comme toute mthode qui se respecte, la mthode __init__() peut tre dote de paramtres. Et dans le
cas de cette mthode particulire quest le constructeur, les paramtres peuvent jouer un rle trs
intressant, parce quils vont permettre dinitialiser certaines de ses variables dinstance au moment
mme de linstanciation de lobjet.
Veuillez donc reprendre lexemple prcdent, en modifiant la dfinition de la mthode __init__()
comme suit :
...
...
...
...

def __init__(self, hh =12, mm =0, ss =0):


self.heure =hh
self.minute =mm
self.seconde =ss

Notre nouvelle mthode __init__() comporte prsent 3 paramtres, avec pour chacun une valeur par
dfaut. Nous obtenons ainsi une classe encore plus perfectionne. Lorsque nous instancions un objet
de cette classe, nous pouvons maintenant initialiser ses principaux attributs laide darguments, au
sein mme de linstruction dinstanciation. Et si nous omettons tout ou partie dentre eux, les attributs
reoivent de toute manire des valeurs par dfaut.
Lorsque lon crit linstruction dinstanciation dun nouvel objet, et que lon veut transmettre des
arguments sa mthode constructeur, il suffit de placer ceux-ci dans les parenthses qui
accompagnent le nom de la classe. On procde donc exactement de la mme manire que lorsque lon
invoque une fonction quelconque.
Voici par exemple la cration et linitialisation simultanes dun nouvel objet Time() :
>>> recreation = Time(10, 15, 18)
>>> recreation.affiche_heure()
10:15:18

Puisque les variables dinstance possdent maintenant des valeurs par dfaut, nous pouvons aussi bien
crer de tels objets Time() en omettant un ou plusieurs arguments :
>>> rentree = Time(10, 30)
>>> rentree.affiche_heure()
10:30:0

ou encore :
>>> rendezVous = Time(hh =18)
>>> rendezVous.affiche_heure()
18:0:0

178

Classes, mthodes, hritage

Exercices

12.1 Dfinissez une classe Domino() qui permette dinstancier des objets simulant les pices dun
jeu de dominos. Le constructeur de cette classe initialisera les valeurs des points prsents sur
les deux faces A et B du domino (valeurs par dfaut = 0).
Deux autres mthodes seront dfinies :
une mthode affiche_points() qui affiche les points prsents sur les deux faces ;
une mthode valeur() qui renvoie la somme des points prsents sur les 2 faces.
Exemples dutilisation de cette classe :
>>> d1 = Domino(2,6)
>>> d2 = Domino(4,3)
>>> d1.affiche_points()
face A : 2 face B : 6
>>> d2.affiche_points()
face A : 4 face B : 3
>>> print("total des points :", d1.valeur() + d2.valeur())
15
>>> liste_dominos = []
>>> for i in range(7):
...
liste_dominos.append(Domino(6, i))
>>> print(liste_dominos[3])
<__main__.Domino object at 0xb758b92c>

etc.
12.2 Dfinissez une classe CompteBancaire(), qui permette dinstancier des objets tels que compte1, compte2, etc. Le constructeur de cette classe initialisera deux attributs dinstance nom et
solde, avec les valeurs par dfaut Dupont et 1000.
Trois autres mthodes seront dfinies :
depot(somme)
permettra dajouter une certaine somme au solde ;
retrait(somme) permettra de retirer une certaine somme du solde ;
affiche()
permettra dafficher le nom du titulaire et le solde de son compte.
Exemples dutilisation de cette classe :
>>> compte1 = CompteBancaire('Duchmol', 800)
>>> compte1.depot(350)
>>> compte1.retrait(200)
>>> compte1.affiche()
Le solde du compte bancaire de Duchmol est de 950 euros.
>>> compte2 = CompteBancaire()
>>> compte2.depot(25)
>>> compte2.affiche()
Le solde du compte bancaire de Dupont est de 1025 euros.

12.3 Dfinissez une classe Voiture() qui permette dinstancier des objets reproduisant le
comportement de voitures automobiles. Le constructeur de cette classe initialisera les
attributs dinstance suivants, avec les valeurs par dfaut indiques :
marque = 'Ford', couleur = 'rouge', pilote = 'personne', vitesse = 0.
Lorsque lon instanciera un nouvel objet Voiture(), on pourra choisir sa marque et sa couleur,
mais pas sa vitesse, ni le nom de son conducteur.
Les mthodes suivantes seront dfinies :
choix_conducteur(nom) permettra de dsigner (ou changer) le nom du conducteur.

La mthode constructeur

179

accelerer(taux, duree) permettra de faire varier la vitesse de la voiture. La variation de


vitesse obtenue sera gale au produit : taux duree. Par exemple, si la voiture acclre au
taux de 1,3 m/s pendant 20 secondes, son gain de vitesse doit tre gal 26 m/s. Des taux
ngatifs seront accepts (ce qui permettra de dclrer). La variation de vitesse ne sera pas
autorise si le conducteur est personne.
affiche_tout() permettra de faire apparatre les proprits prsentes de la voiture, cest-dire sa marque, sa couleur, le nom de son conducteur, sa vitesse.
Exemples dutilisation de cette classe :
>>> a1 = Voiture('Peugeot', 'bleue')
>>> a2 = Voiture(couleur = 'verte')
>>> a3 = Voiture('Mercedes')
>>> a1.choix_conducteur('Romo')
>>> a2.choix_conducteur('Juliette')
>>> a2.accelerer(1.8, 12)
>>> a3.accelerer(1.9, 11)
Cette voiture n'a pas de conducteur !
>>> a2.affiche_tout()
Ford verte pilote par Juliette, vitesse = 21.6 m/s.
>>> a3.affiche_tout()
Mercedes rouge pilote par personne, vitesse = 0 m/s.

12.4 Dfinissez une classe Satellite() qui permette dinstancier des objets simulant des satellites
artificiels lancs dans lespace, autour de la terre. Le constructeur de cette classe initialisera
les attributs dinstance suivants, avec les valeurs par dfaut indiques :
masse = 100,

vitesse = 0.

Lorsque lon instanciera un nouvel objet Satellite(), on pourra choisir son nom, sa masse et sa
vitesse.
Les mthodes suivantes seront dfinies :
impulsion(force, duree) permettra de faire varier la vitesse du satellite. Pour savoir
comment, rappelez-vous votre cours de physique : la variation de vitesse v subie par un
Ft
objet de masse m soumis laction dune force F pendant un temps t vaut v=
.
m
Par exemple : un satellite de 300 kg qui subit une force de 600 Newtons pendant 10 secondes
voit sa vitesse augmenter (ou diminuer) de 20 m/s.
affiche_vitesse() affichera le nom du satellite et sa vitesse courante.
energie() renverra au programme appelant la valeur de lnergie cintique du satellite.
mv 2
Rappel : lnergie cintique se calcule laide de la formule E c =
2
Exemples dutilisation de cette classe :
>>> s1 = Satellite('Zo', masse =250, vitesse =10)
>>> s1.impulsion(500, 15)
>>> s1.affiche_vitesse()
vitesse du satellite Zo = 40 m/s.
>>> print (s1.energie)
200000
>>> s1.impulsion(500, 15)
>>> s1.affiche_vitesse()
vitesse du satellite Zo = 70 m/s.
>>> print (s1.energie)
612500

180

Classes, mthodes, hritage

Espaces de noms des classes et instances


Vous avez appris prcdemment (voir page 66) que les variables dfinies lintrieur dune fonction
sont des variables locales, inaccessibles aux instructions qui se trouvent lextrieur de la fonction.
Cela vous permet dutiliser les mmes noms de variables dans diffrentes parties dun programme,
sans risque dinterfrence.
Pour dcrire la mme chose en dautres termes, nous pouvons dire que chaque fonction possde son
propre espace de noms, indpendant de lespace de noms principal.
Vous avez appris galement que les instructions se trouvant lintrieur dune fonction peuvent
accder aux variables dfinies au niveau principal, mais en consultation seulement : elles peuvent utiliser
les valeurs de ces variables, mais pas les modifier ( moins de faire appel linstruction global).
Il existe donc une sorte de hirarchie entre les espaces de noms. Nous allons constater la mme chose
propos des classes et des objets. En effet :

Chaque classe possde son propre espace de noms. Les variables qui en font partie sont appeles
variables de classe ou attributs de classe.
Chaque objet instance (cr partir dune classe) obtient son propre espace de noms. Les variables
qui en font partie sont appeles variables dinstance ou attributs dinstance.
Les classes peuvent utiliser (mais pas modifier) les variables dfinies au niveau principal.
Les instances peuvent utiliser (mais pas modifier) les variables dfinies au niveau de la classe et les
variables dfinies au niveau principal.
Considrons par exemple la classe Time() dfinie prcdemment. la page 177, nous avons instanci
trois objets de cette classe : recreation, rentree et rendezVous. Chacun a t initialis avec des
valeurs diffrentes, indpendantes. Nous pouvons modifier et rafficher ces valeurs volont dans
chacun de ces trois objets, sans que lautre nen soit affect :
>>> recreation.heure = 12
>>> rentree.affiche_heure()
10:30:0
>>> recreation.affiche_heure()
12:15:18

Veuillez prsent encoder et tester lexemple ci-dessous :


>>> class Espaces(object):
...
aa = 33
...
def affiche(self):
...
print(aa, Espaces.aa, self.aa)
...
>>> aa = 12
>>> essai = Espaces()
>>> essai.aa = 67
>>> essai.affiche()
12 33 67
>>> print(aa, Espaces.aa, essai.aa)
12 33 67

#
#
#
#

1
2
3
4

#
#
#
#

5
6
7
8

# 9

Dans cet exemple, le mme nom aa est utilis pour dfinir trois variables diffrentes : une dans lespace de noms de la classe ( la ligne 2), une autre dans lespace de noms principal ( la ligne 5), et enfin

Espaces de noms des classes et instances

181

une dernire dans lespace de nom de linstance ( la ligne 7).


La ligne 4 et la ligne 9 montrent comment vous pouvez accder ces trois espaces de noms (de lintrieur dune classe, ou au niveau principal), en utilisant la qualification par points. Notez encore une
fois lutilisation de self pour dsigner linstance lintrieur de la dfinition dune classe.

Hritage
Les classes constituent le principal outil de la programmation oriente objet (Object Oriented
Programming ou OOP), qui est considre de nos jours comme la technique de programmation la plus
performante. Lun des principaux atouts de ce type de programmation rside dans le fait que lon peut
toujours se servir dune classe prexistante pour en crer une nouvelle, qui hritera toutes ses
proprits mais pourra modifier certaines dentre elles et/ou y ajouter les siennes propres. Le procd
sappelle drivation. Il permet de crer toute une hirarchie de classes allant du gnral au particulier.
Nous pouvons par exemple dfinir une classe Mammifere(), qui contienne un ensemble de
caractristiques propres ce type danimal. partir de cette classe parente, nous pouvons driver une
ou plusieurs classes filles, comme : une classe Primate(), une classe Rongeur(), une classe Carnivore(),
etc., qui hriteront toutes les caractristiques de la classe Mammifere(), en y ajoutant leurs
spcificits.
Au dpart de la classe Carnivore(), nous pouvons ensuite driver une classe Belette(), une classe
Loup(), une classe Chien(), etc., qui hriteront encore une fois toutes les caractristiques de la classe
parente avant dy ajouter les leurs. Exemple :
>>> class Mammifere(object):
...
caract1 = "il allaite ses petits ;"
>>> class Carnivore(Mammifere):
...
caract2 = "il se nourrit de la chair de ses proies ;"
>>> class Chien(Carnivore):
...
caract3 = "son cri s'appelle aboiement ;"
>>> mirza = Chien()
>>> print(mirza.caract1, mirza.caract2, mirza.caract3)
il allaite ses petits ; il se nourrit de la chair de ses proies ;
son cri s'appelle aboiement ;

Dans cet exemple, nous voyons que lobjet mirza , qui est une instance de la classe Chien(), hrite non
seulement lattribut dfini pour cette classe, mais galement les attributs dfinis pour les classes
parentes.
Vous voyez galement dans cet exemple comment il faut procder pour driver une classe partir
dune classe parente : on utilise linstruction class, suivie comme dhabitude du nom que lon veut
attribuer la nouvelle classe, et on place entre parenthses le nom de la classe parente. Les classes les
plus fondamentales drivent quant elles de lobjet anctre object.

182

Classes, mthodes, hritage

Notez bien que les attributs utiliss dans cet exemple sont des attributs des classes (et non des
attributs dinstances). Linstance mirza peut accder ces attributs, mais pas les modifier :
>>> mirza.caract2 = "son corps est couvert de poils ;"
>>> print(mirza.caract2)
son corps est couvert de poils ;
>>> fido = Chien()
>>> print(fido.caract2)
il se nourrit de la chair de ses proies ;

#
#
#
#
#
#

1
2
3
4
5
6

Dans ce nouvel exemple, la ligne 1 ne modifie pas lattribut caract2 de la classe Carnivore(),
contrairement ce que lon pourrait penser au vu de la ligne 3. Nous pouvons le vrifier en crant une
nouvelle instance fido (lignes 4 6).
Si vous avez bien assimil les paragraphes prcdents, vous aurez compris que linstruction de la ligne
1 cre une nouvelle variable dinstance associe seulement lobjet mirza. Il existe donc ds ce
moment deux variables avec le mme nom caract2 : lune dans lespace de noms de lobjet mirza, et
lautre dans lespace de noms de la classe Carnivore().
Comment faut-il alors interprter ce qui sest pass aux lignes 2 et 3 ?
Comme nous lavons vu plus haut, linstance mirza peut accder aux variables situes dans son propre
espace de noms, mais aussi celles qui sont situes dans les espaces de noms de toutes les classes
parentes. Sil existe des variables aux noms identiques dans plusieurs de ces espaces, laquelle sera
slectionne lors de lexcution dune instruction comme celle de la ligne 2 ?
Pour rsoudre ce conflit, Python respecte une rgle de priorit fort simple. Lorsquon lui demande
dutiliser la valeur dune variable nomme alpha, par exemple, il commence par rechercher ce nom
dans lespace local (le plus interne , en quelque sorte). Si une variable alpha est trouve dans lespace local, cest celle-l qui est utilise, et la recherche sarrte. Sinon, Python examine lespace de
noms de la structure parente, puis celui de la structure grand-parente, et ainsi de suite jusquau
niveau principal du programme.
la ligne 2 de notre exemple, cest donc la variable dinstance qui sera utilise. la ligne 5, par contre,
cest seulement au niveau de la classe grand-parente quune variable rpondant au nom caract2 peut
tre trouve. Cest donc celle-l qui est affiche.

Hritage et polymorphisme
Analysez soigneusement le script de la page suivante. Il met en uvre plusieurs concepts dcrits
prcdemment, en particulier le concept dhritage.
Pour bien comprendre ce script, il faut cependant dabord vous rappeler quelques notions
lmentaires de chimie. Dans votre cours de chimie, vous avez certainement d apprendre que les
atomes sont des entits, constitus dun certain nombre de protons (particules charges dlectricit
positive), dlectrons (chargs ngativement) et de neutrons (neutres).
Le type datome (ou lment) est dtermin par le nombre de protons, que lon appelle galement numro atomique. Dans son tat fondamental, un atome contient autant dlectrons que de protons, et par

Hritage et polymorphisme

183

consquent il est lectriquement neutre. Il possde galement un nombre variable de neutrons, mais
ceux-ci ninfluencent en aucune manire la charge lectrique globale.
Dans certaines circonstances, un atome peut gagner ou perdre des lectrons. Il acquiert de ce fait une
charge lectrique globale, et devient alors un ion (il sagit dun ion ngatif si latome a gagn un ou
plusieurs lectrons, et dun ion positif sil en a perdu). La charge lectrique dun ion est gale la
diffrence entre le nombre de protons et le nombre dlectrons quil contient.
Le script reproduit la page suivante gnre des objets Atome() et des objets Ion(). Nous avons
rappel ci-dessus quun ion est simplement un atome modifi. Dans notre programmation, la classe qui
dfinit les objets Ion() sera donc une classe drive de la classe Atome() : elle hritera delle tous ses
attributs et toutes ses mthodes, en y ajoutant les siennes propres.
Lune de ces mthodes ajoutes (la mthode affiche()) remplace une mthode de mme nom hrite
de la classe Atome(). Les classes Atome() et Ion() possdent donc chacune une mthode de mme
nom, mais qui effectuent un travail diffrent. On parle dans ce cas de polymorphisme. On pourra dire
galement que la mthode affiche() de la classe Atome() a t surcharge.
Il sera videmment possible dinstancier un nombre quelconque datomes et dions partir de ces deux
classes. Or lune dentre elles, la classe Atome(), doit contenir une version simplifie du tableau priodique des lments (tableau de Mendeleev), de faon pouvoir attribuer un nom dlment chimique,
ainsi quun nombre de neutrons, chaque objet gnr. Comme il nest pas souhaitable de recopier
tout ce tableau dans chacune des instances, nous le placerons dans un attribut de classe. Ainsi ce tableau
nexistera quen un seul endroit en mmoire, tout en restant accessible tous les objets qui seront
produits partir de cette classe.
Voyons concrtement comment toutes ces ides sarticulent :
class Atome:
"""atomes simplifis, choisis parmi les 10 premiers lments du TP"""
table =[None, ('hydrogne',0),('hlium',2),('lithium',4),
('bryllium',5),('bore',6),('carbone',6),('azote',7),
('oxygne',8),('fluor',10),('non',10)]
def __init__(self, nat):
"le n atomique dtermine le n. de protons, d'lectrons et de neutrons"
self.np, self.ne = nat, nat
# nat = numro atomique
self.nn = Atome.table[nat][1]
def affiche(self):
print()
print("Nom de l'lment :", Atome.table[self.np][0])
print("{0} protons, {1} lectrons, {2} neutrons".\
format(self.np, self.ne, self.nn))
class Ion(Atome):
"""les ions sont des atomes qui ont gagn ou perdu des lectrons"""
def __init__(self, nat, charge):
"le n atomique et la charge lectrique dterminent l'ion"
Atome.__init__(self, nat)
self.ne = self.ne - charge
self.charge = charge

184

Classes, mthodes, hritage


def affiche(self):
Atome.affiche(self)
print("Particule lectrise. Charge =", self.charge)

### Programme principal : ###


a1 = Atome(5)
a2 = Ion(3, 1)
a3 = Ion(8, -2)
a1.affiche()
a2.affiche()
a3.affiche()

Lexcution de ce script provoque laffichage suivant :


Nom de l'lment : bore
5 protons, 5 lectrons, 6 neutrons
Nom de l'lment : lithium
3 protons, 2 lectrons, 4 neutrons
Particule lectrise. Charge = 1
Nom de l'lment : oxygne
8 protons, 10 lectrons, 8 neutrons
Particule lectrise. Charge = -2

Au niveau du programme principal, vous pouvez constater que lon instancie les objets Atome() en
fournissant leur numro atomique (lequel doit tre compris entre 1 et 10). Pour instancier des objets
Ion(), par contre, on doit fournir un numro atomique et une charge lectrique globale (positive ou
ngative). La mme mthode affiche() fait apparatre les proprits de ces objets, quil sagisse
datomes ou dions, avec dans le cas de lion une ligne supplmentaire (polymorphisme).

Commentaires
La dfinition de la classe Atome() commence par lassignation de la variable table. Une variable
dfinie cet endroit fait partie de lespace de noms de la classe. Cest donc un attribut de classe, dans
lequel nous plaons une liste dinformations concernant les 10 premiers lments du tableau
priodique de Mendeleev.
Pour chacun de ces lments, la liste contient un tuple : (nom de llment, nombre de neutrons),
lindice qui correspond au numro atomique. Comme il nexiste pas dlment de numro atomique
zro, nous avons plac lindice zro dans la liste, lobjet spcial None. Nous aurions pu placer cet
endroit nimporte quelle autre valeur, puisque cet indice ne sera pas utilis. Lobjet None de Python
nous semble cependant particulirement explicite.
Viennent ensuite les dfinitions de deux mthodes :

Le constructeur __init__() sert essentiellement ici gnrer trois attributs dinstance, destins
mmoriser respectivement les nombres de protons, dlectrons et de neutrons pour chaque objet
atome construit partir de cette classe (rappelez-vous que les attributs dinstance sont des
variables lies au paramtre self).
Notez au passage la technique utilise pour obtenir le nombre de neutrons partir de lattribut de

Hritage et polymorphisme

185

classe, en mentionnant le nom de la classe elle-mme dans une qualification par points, comme
dans linstruction : self.nn = Atome.table[nat][1].
La mthode affiche() utilise la fois les attributs dinstance, pour retrouver les nombres de
protons, dlectrons et de neutrons de lobjet courant, et lattribut de classe (lequel est commun
tous les objets) pour en extraire le nom dlment correspondant.
La dfinition de la classe Ion() inclut dans ses parenthses le nom de la classe Atome() qui prcde.
Les mthodes de cette classe sont des variantes de celles de la classe Atome(). Elles devront donc
vraisemblablement faire appel celles-ci. Cette remarque est importante : comment peut-on, lintrieur de la dfinition dune classe, faire appel une mthode dfinie dans une autre classe ?
Il ne faut pas perdre de vue, en effet, quune mthode se rattache toujours linstance qui sera
gnre partir de la classe (instance reprsente par self dans la dfinition). Si une mthode doit
faire appel une autre mthode dfinie dans une autre classe, il faut pouvoir lui transmettre la
rfrence de linstance laquelle elle doit sassocier. Comment faire ? Cest trs simple :
Lorsque dans la dfinition dune classe, on souhaite faire appel une mthode dfinie
dans une autre classe, il suffit de linvoquer directement, via cette autre classe, en lui
transmettant la rfrence de linstance comme premier argument.

Cest ainsi que dans notre script, par exemple, la mthode affiche() de la classe Ion() peut faire appel
la mthode affiche() de la classe Atome() : les informations affiches seront bien celles de lobjet-ion
courant, puisque sa rfrence a t transmise dans linstruction dappel :
Atome.affiche(self)

Dans cette instruction, self est bien entendu la rfrence de linstance courante.
De la mme manire (vous en verrez de nombreux autres exemples plus loin), la mthode constructeur
de la classe Ion() fait appel la mthode constructeur de sa classe parente, dans :
Atome.__init__(self, nat)

Cet appel est ncessaire, afin que les objets de la classe Ion() soient initialiss de la mme manire que
les objets de la classe Atome(). Si nous neffectuons pas cet appel, les objets-ions nhriteront pas
automatiquement les attributs ne, np et nn, car ceux ci sont des attributs dinstance crs par la
mthode constructeur de la classe Atome(), et celle-ci nest pas invoque automatiquement lorsquon
instancie des objets dune classe drive.
Comprenez donc bien que lhritage ne concerne que les classes, et non les instances de ces classes.
Lorsque nous disons quune classe drive hrite toutes les proprits de sa classe parente, cela ne
signifie pas que les proprits des instances de la classe parente sont automatiquement transmises aux
instances de la classe fille. En consquence, retenez bien que :
Dans la mthode constructeur dune classe drive, il faut presque toujours prvoir un
appel la mthode constructeur de sa classe parente.

186

Classes, mthodes, hritage

Modules contenant des bibliothques de classes

187

Modules contenant des bibliothques de classes


Vous connaissez dj depuis longtemps lutilit des modules Python (cf. pages 50 et 71). Vous savez
quils servent regrouper des bibliothques de classes et de fonctions. titre dexercice de rvision,
vous allez crer vous-mme un nouveau module de classes, en encodant les lignes dinstruction
ci-dessous dans un fichier-module que vous nommerez formes.py :
class Rectangle(object):
"Classe de rectangles"
def __init__(self, longueur =0, largeur =0):
self.L = longueur
self.l = largeur
self.nom ="rectangle"
def perimetre(self):
return "({0:d} + {1:d}) * 2 = {2:d}".\
format(self.L, self.l, (self.L + self.l)*2)
def surface(self):
return "{0:d} * {1:d} = {2:d}".format(self.L, self.l, self.L*self.l)
def mesures(self):
print("Un {0} de {1:d} sur {2:d}".format(self.nom, self.L, self.l))
print("a une surface de {0}".format(self.surface()))
print("et un primtre de {0}\n".format(self.perimetre()))
class Carre(Rectangle):
"Classe de carrs"
def __init__(self, cote):
Rectangle.__init__(self, cote, cote)
self.nom ="carr"
if __name__ == "__main__":
r1 = Rectangle(15, 30)
r1.mesures()
c1 = Carre(13)
c1.mesures()

Une fois ce module enregistr, vous pouvez lutiliser de deux manires : soit vous en lancez lexcution
comme celle dun programme ordinaire, soit vous limportez dans un script quelconque ou depuis la
ligne de commande, pour en utiliser les classes. Prenons un exemple.
>>> import formes
>>> f1 = formes.Rectangle(27, 12)
>>> f1.mesures()
Un rectangle de 27 sur 12
a une surface de 27 * 12 = 324
et un primtre de (27 + 12) * 2 = 78
>>> f2 = formes.Carre(13)
>>> f2.mesures()
Un carr de 13 sur 13
a une surface de 13 * 13 = 169
et un primtre de (13 + 13) * 2 = 52

On voit dans ce script que la classe Carre() est construite par drivation partir de la classe Rectangle() dont elle hrite toutes les caractristiques. En dautres termes, la classe Carre() est une classe
fille de la classe Rectangle().

188

Classes, mthodes, hritage

Vous pouvez remarquer encore une fois que le constructeur de la classe Carre() doit faire appel au
constructeur de sa classe parente ( Rectangle.__init__(self, ...) ), en lui transmettant la
rfrence de linstance (self) comme premier argument.
Quant linstruction :
if __name__ == "__main__":

place la fin du module, elle sert dterminer si le module est lanc en tant que programme
autonome (auquel cas les instructions qui suivent doivent tre excutes), ou au contraire utilis
comme une bibliothque de classes importe ailleurs. Dans ce cas cette partie du code est sans effet.
Exercices
12.5 Dfinissez une classe Cercle(). Les objets construits partir de cette classe seront des cercles
de tailles varies. En plus de la mthode constructeur (qui utilisera donc un paramtre rayon),
vous dfinirez une mthode surface(), qui devra renvoyer la surface du cercle.

Dfinissez ensuite une classe Cylindre() drive de la prcdente. Le constructeur de cette


nouvelle classe comportera les deux paramtres rayon et hauteur. Vous y ajouterez une
mthode volume() qui devra renvoyer le volume du cylindre (rappel : volume dun cylindre =
surface de section hauteur).
Exemple dutilisation de cette classe :
>>> cyl = Cylindre(5, 7)
>>> print(cyl.surface())
78.54
>>> print(cyl.volume())
549.78

12.6 Compltez lexercice prcdent en lui ajoutant encore une classe Cone(), qui devra driver
cette fois de la classe Cylindre(), et dont le constructeur comportera lui aussi les deux
paramtres rayon et hauteur. Cette nouvelle classe possdera sa propre mthode volume(),
laquelle devra renvoyer le volume du cne (rappel : volume dun cne = volume du cylindre
correspondant divis par 3).
Exemple dutilisation de cette classe :
>>> co = Cone(5,7)
>>> print(co.volume())
183.26

12.7 Dfinissez une classe JeuDeCartes() permettant dinstancier des objets dont le comportement
soit similaire celui dun vrai jeu de cartes. La classe devra comporter au moins les quatre
mthodes suivantes :
mthode constructeur : cration et remplissage dune liste de 52 lments, qui sont
eux-mmes des tuples de 2 entiers. Cette liste de tuples contiendra les caractristiques de
chacune des 52 cartes. Pour chacune delles, il faut en effet mmoriser sparment un entier
indiquant la valeur (2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, les 4 dernires valeurs tant celles
des valet, dame, roi et as), et un autre entier indiquant la couleur de la carte (cest--dire
3,2,1,0 pour Cur, Carreau, Trfle et Pique).
Dans une telle liste, llment (11,2) dsigne donc le valet de Trfle, et la liste termine doit

Modules contenant des bibliothques de classes

189

tre du type :
[(2, 0), (3,0), (3,0), (4,0), ..... (12,3), (13,3), (14,3)]

mthode nom_carte() : cette mthode doit renvoyer, sous la forme dune chane, lidentit
dune carte quelconque dont on lui a fourni le tuple descripteur en argument. Par exemple,
linstruction : print(jeu.nom_carte((14, 3))) doit provoquer laffichage de : As de
pique

mthode battre() : comme chacun sait, battre les cartes consiste les mlanger.
Cette mthode sert donc mlanger les lments de la liste contenant les cartes, quel quen
soit le nombre.
mthode tirer() : lorsque cette mthode est invoque, une carte est retire du jeu. Le tuple
contenant sa valeur et sa couleur est renvoy au programme appelant. On retire toujours la
premire carte de la liste. Si cette mthode est invoque alors quil ne reste plus aucune
carte dans la liste, il faut alors renvoyer lobjet spcial None au programme appelant.
Exemple dutilisation de la classe JeuDeCartes() :
jeu = JeuDeCartes()
jeu.battre()
for n in range(53):
c = jeu.tirer()
if c == None:
print('Termin !')
else:
print(jeu.nom_carte(c))

# instanciation d'un objet


# mlange des cartes
# tirage des 52 cartes :
# il ne reste plus aucune carte
# dans la liste
# valeur et couleur de la carte

12.8 Complment de lexercice prcdent : dfinir deux joueurs A et B. Instancier deux jeux de
cartes (un pour chaque joueur) et les mlanger. Ensuite, laide dune boucle, tirer 52 fois une
carte de chacun des deux jeux et comparer leurs valeurs. Si cest la premire des deux qui a la
valeur la plus leve, on ajoute un point au joueur A. Si la situation contraire se prsente, on
ajoute un point au joueur B. Si les deux valeurs sont gales, on passe au tirage suivant. Au
terme de la boucle, comparer les comptes de A et B pour dterminer le gagnant.
12.9 crivez un nouveau script qui rcupre le code de lexercice 12.2 (compte bancaire) en limportant comme un module. Dfinissez-y une nouvelle classe CompteEpargne(), drivant de la
classe CompteBancaire() importe, qui permette de crer des comptes dpargne rapportant
un certain intrt au cours du temps. Pour simplifier, nous admettrons que ces intrts sont
calculs tous les mois.
Le constructeur de votre nouvelle classe devra initialiser un taux dintrt mensuel par dfaut
gal 0,3 %. Une mthode changeTaux(valeur) devra permettre de modifier ce taux
volont.
Une mthode capitalisation(nombreMois) devra :
afficher le nombre de mois et le taux dintrt pris en compte ;
calculer le solde atteint en capitalisant les intrts composs, pour le taux et le nombre de
mois qui auront t choisis.
Exemple dutilisation de la nouvelle classe :
>>> c1 = CompteEpargne('Duvivier', 600)
>>> c1.depot(350)

190

Classes, mthodes, hritage


>>> c1.affiche()
Le solde du compte bancaire de Duvivier est de 950 euros.
>>> c1.capitalisation(12)
Capitalisation sur 12 mois au taux mensuel de 0.3 %.
>>> c1.affiche()
Le solde du compte bancaire de Duvivier est de 984.769981274 euros.
>>> c1.changeTaux(.5)
>>> c1.capitalisation(12)
Capitalisation sur 12 mois au taux mensuel de 0.5 %.
>>> c1.affiche()
Le solde du compte bancaire de Duvivier est de 1045.50843891 euros.

13
Classes et interfaces graphiques

13

La programmation oriente objet convient particulirement bien au dveloppement dapplications


avec interface graphique. Des bibliothques de classes comme tkinter ou wxPython fournissent une
base de widgets trs toffe, que nous pouvons adapter nos besoins par drivation. Dans ce
chapitre, nous allons utiliser nouveau la bibliothque tkinter, mais en appliquant les concepts
dcrits dans les pages prcdentes, et en nous efforant de mettre en vidence les avantages quapporte lorientation objet dans nos programmes.

Code des couleurs : un petit projet bien encapsul


Nous allons commencer par un petit projet qui nous a t inspir par le cours dinitiation llectronique. Lapplication que nous dcrivons ci-aprs permet de retrouver rapidement le code de trois
couleurs qui correspond une rsistance lectrique de valeur bien dtermine.
Pour rappel, la fonction des rsistances lectriques consiste sopposer ( rsister) plus ou moins bien
au passage du courant. Les rsistances se prsentent concrtement sous la forme de petites pices
tubulaires cercles de bandes de couleur (en gnral 3). Ces bandes de couleur indiquent la valeur
numrique de la rsistance, en fonction du code suivant :
Chaque couleur correspond conventionnellement lun des chiffres de zro neuf :
Noir = 0 ; Brun = 1 ; Rouge = 2 ; Orange = 3 ; Jaune = 4 ;
Vert = 5 ; Bleu = 6 ; Violet = 7 ; Gris = 8 ; Blanc = 9.
On oriente la rsistance de manire telle que les bandes colores soient places gauche. La valeur de
la rsistance exprime en ohms () sobtient en lisant ces bandes colores galement partir de la
gauche : les deux premires bandes indiquent les deux premiers chiffres de la valeur numrique ; il
faut ensuite accoler ces deux chiffres un nombre de zros gal lindication fournie par la troisime
bande.
Exemple concret : supposons qu partir de la gauche, les bandes colores soient jaune, violette et
verte.
La valeur de cette rsistance est 4700000 , ou 4700 k, ou encore 4,7 M .

192

Classes et interfaces graphiques

Ce systme ne permet videmment de prciser une valeur numrique quavec deux chiffres
significatifs seulement. Il est toutefois considr comme largement suffisant pour la plupart des
applications lectroniques ordinaires (radio, TV, etc.).
Cahier des charges de notre programme
Notre application doit faire apparatre une fentre
comportant un dessin de la rsistance, ainsi quun champ
dentre dans lequel lutilisateur peut encoder une
valeur numrique. Un bouton <Montrer> dclenche la
modification du dessin de la rsistance, de telle faon
que les trois bandes de couleur se mettent en accord
avec la valeur numrique introduite.
Contrainte : Le programme doit accepter toute entre
numrique fournie sous forme entire ou relle, dans les
limites de 10 1011 . Par exemple, une valeur telle que
4.78e6 doit tre accepte et arrondie correctement, cest--dire convertie en 4800000 .
Mise en uvre concrte
Nous construisons le corps de cette application simple sous la forme dune classe. Nous voulons vous
montrer ainsi comment une classe peut servir despace de noms commun, dans lequel vous pouvez encapsuler vos variables et nos fonctions. Le principal intrt de procder ainsi est que cela vous permet
de vous passer de variables globales. En effet :

Mettre en route lapplication se rsumera instancier un objet de cette classe.


Les fonctions que lon voudra y mettre en uvre seront les mthodes de cet objet-application.
lintrieur de ces mthodes, il suffira de rattacher un nom de variable au paramtre self pour
que cette variable soit accessible de partout lintrieur de lobjet. Une telle variable dinstance est
donc tout fait lquivalent dune variable globale (mais seulement lintrieur de lobjet),
puisque toutes les autres mthodes de cet objet peuvent y accder par lintermdiaire de self.
1# class Application(object):
2#
def __init__(self):
3#
"""Constructeur de la fentre principale"""
4#
self.root =Tk()
5#
self.root.title('Code des couleurs')
6#
self.dessineResistance()
7#
Label(self.root, text ="Entrez la valeur de la rsistance, en ohms :").\
8#
grid(row =2, column =1, columnspan =3)
9#
Button(self.root, text ='Montrer', command =self.changeCouleurs).\
10#
grid(row =3, column =1)
11#
Button(self.root, text ='Quitter', command =self.root.quit).\
12#
grid(row =3, column =3)
13#
self.entree = Entry(self.root, width =14)
14#
self.entree.grid(row =3, column =2)
15#
# Code des couleurs pour les valeurs de zro neuf :
16#
self.cc = ['black','brown','red','orange','yellow',
17#
'green','blue','purple','grey','white']
18#
self.root.mainloop()
19#

Code des couleurs : un petit projet bien encapsul

193

20#
def dessineResistance(self):
21#
"""Canevas avec un modle de rsistance trois lignes colores"""
22#
self.can = Canvas(self.root, width=250, height =100, bg ='ivory')
23#
self.can.grid(row =1, column =1, columnspan =3, pady =5, padx =5)
24#
self.can.create_line(10, 50, 240, 50, width =5)
# fils
25#
self.can.create_rectangle(65, 30, 185, 70, fill ='light grey', width =2)
26#
# Dessin des trois lignes colores (noires au dpart) :
27#
self.ligne =[]
# on mmorisera les trois lignes dans 1 liste
28#
for x in range(85,150,24):
29#
self.ligne.append(self.can.create_rectangle(x,30,x+12,70,
30#
fill='black',width=0))
31#
32#
def changeCouleurs(self):
33#
"""Affichage des couleurs correspondant la valeur entre"""
34#
self.v1ch = self.entree.get()
# cette mthode renvoie une chane
35#
try:
36#
v = float(self.v1ch)
# conversion en valeur numrique
37#
except:
38#
err =1
# erreur : entre non numrique
39#
else:
40#
err =0
41#
if err ==1 or v < 10 or v > 1e11 :
42#
self.signaleErreur()
# entre incorrecte ou hors limites
43#
else:
44#
li =[0]*3
# liste des 3 codes afficher
45#
logv = int(log10(v))
# partie entire du logarithme
46#
ordgr = 10**logv
# ordre de grandeur
47#
# extraction du premier chiffre significatif :
48#
li[0] = int(v/ordgr)
# partie entire
49#
decim = v/ordgr - li[0]
# partie dcimale
50#
# extraction du second chiffre significatif :
51#
li[1] = int(decim*10 +.5)
# +.5 pour arrondir correctement
52#
# nombre de zros accoler aux 2 chiffres significatifs :
53#
li[2] = logv -1
54#
# Coloration des 3 lignes :
55#
for n in range(3):
56#
self.can.itemconfigure(self.ligne[n], fill =self.cc[li[n]])
57#
58#
def signaleErreur(self):
59#
self.entree.configure(bg ='red')
# colorer le fond du champ
60#
self.root.after(1000, self.videEntree)
# aprs 1 seconde, effacer
61#
62#
def videEntree(self):
63#
self.entree.configure(bg ='white')
# rtablir le fond blanc
64#
self.entree.delete(0, len(self.v1ch))
# enlever les car. prsents
65#
66# # Programme principal :
67# if __name__ == '__main__':
68#
from tkinter import *
69#
from math import log10
# logarithmes en base 10
70#
f = Application()
# instanciation de l'objet application

Commentaires

Ligne 1 : La classe est dfinie comme une nouvelle classe indpendante (elle ne drive daucune
classe parente prexistante, mais seulement de object, anctre de toutes les classes).
Lignes 2 14 : Le constructeur de la classe instancie les widgets ncessaires : espace graphique,
libells et boutons. Afin damliorer la lisibilit du programme, cependant, nous avons plac linstanciation du canevas (avec le dessin de la rsistance) dans une mthode distincte : dessineResistance(). Veuillez remarquer aussi que pour obtenir un code plus compact, nous ne mmorisons

194

Classes et interfaces graphiques


pas les boutons et le libell dans des variables (comme cela a t expliqu la page 98), parce que
nous ne souhaitons pas y faire rfrence ailleurs dans le programme. Le positionnement des
widgets dans la fentre utilise la mthode grid() dcrite la page 95.
Lignes 15-17 : Le code des couleurs est mmoris dans une simple liste.
Ligne 18 : La dernire instruction du constructeur dmarre lapplication. Si vous prfrez
dmarrer lapplication indpendamment de sa cration, vous devez supprimer cette ligne, et
reporter lappel mainloop() au niveau principal du programme, en ajoutant une instruction :
f.root.mainloop() la ligne 71.
Lignes 20 30 : Le dessin de la rsistance se compose dune ligne et dun premier rectangle gris
clair, pour le corps de la rsistance et ses deux fils. Trois autres rectangles figureront les bandes
colores que le programme devra modifier en fonction des entres de lutilisateur. Ces bandes sont
noires au dpart ; elles sont rfrences dans la liste self.ligne.
Lignes 32 53 : Ces lignes contiennent lessentiel de la fonctionnalit du programme. Lentre
brute fournie par lutilisateur est accepte sous la forme dune chane de caractres.
la ligne 36, on essaie de convertir cette chane en une valeur numrique de type float. Si la
conversion choue, on mmorise lerreur. Si lon dispose bien dune valeur numrique, on vrifie
ensuite quelle se situe effectivement dans lintervalle autoris (de 10 1011 ). Si une erreur est
dtecte, on signale lutilisateur que son entre est incorrecte en colorant de rouge le fond du
champ dentre, qui est ensuite vid de son contenu (lignes 55 61).
Lignes 45-46 : Les mathmatiques viennent notre secours pour extraire de la valeur numrique
son ordre de grandeur (cest--dire lexposant de 10 le plus proche). Veuillez consulter un ouvrage
de mathmatiques pour de plus amples explications concernant les logarithmes.
Lignes 47-48 : Une fois connu lordre de grandeur, il devient relativement facile dextraire du
nombre trait ses deux premiers chiffres significatifs. Exemple : supposons que la valeur entre
soit 31687. Le logarithme de ce nombre est 4,50088... dont la partie entire (4) nous donne lordre
de grandeur de la valeur entre (soit 104). Pour extraire de celle-ci son premier chiffre significatif,
il suffit de la diviser par 104, soit 10000, et de conserver seulement la partie entire du rsultat (3).
Lignes 49 51 : Le rsultat de la division effectue dans le paragraphe prcdent est 3,1687. Nous
rcuprons la partie dcimale de ce nombre la ligne 49, soit 0,1687 dans notre exemple. Si nous
le multiplions par dix, ce nouveau rsultat comporte une partie entire qui nest rien dautre que
notre second chiffre significatif (1 dans notre exemple).
Nous pourrions facilement extraire ce dernier chiffre, mais puisque cest le dernier, nous
souhaitons encore quil soit correctement arrondi. Pour ce faire, il suffit dajouter une demi unit
au produit de la multiplication par dix, avant den extraire la valeur entire. Dans notre exemple,
en effet, ce calcul donnera donc 1,687 + 0,5 = 2,187 , dont la partie entire (2) est bien la valeur
arrondie recherche.
Ligne 53 : Le nombre de zros accoler aux deux chiffres significatifs correspond au calcul de
lordre de grandeur. Il suffit de retirer une unit au logarithme.
Ligne 56 : Pour attribuer une nouvelle couleur un objet dj dessin dans un canevas, on utilise
la mthode itemconfigure(). Nous utilisons donc cette mthode pour modifier loption fill de
chacune des bandes colores, en utilisant les noms de couleur extraits de la liste self.cc grce
aux trois indices li[1], li[2] et li[3] qui contiennent les 3 chiffres correspondants.

Code des couleurs : un petit projet bien encapsul

195

Exercices
13.1 Modifiez le script ci-dessus de telle manire que le fond dimage devienne bleu clair (light blue),
que le corps de la rsistance devienne beige (beige), que le fil de cette rsistance soit plus fin, et
que les bandes colores indiquant la valeur soient plus larges.

13.2 Modifiez le script ci-dessus de telle manire que limage dessine soit deux fois plus grande.
13.3 Modifiez le script ci-dessus de telle manire quil devienne possible dentrer aussi des valeurs
de rsistances comprises entre 1 et 10 . Pour ces valeurs, le premier anneau color devra
rester noir, les deux autres indiqueront la valeur en et diximes d.
13.4 Modifiez le script ci-dessus de telle faon que le bouton <Montrer> ne soit plus ncessaire.
Dans votre script modifi, il suffira de frapper <Enter> aprs avoir entr la valeur de la
rsistance, pour que laffichage sactive.
13.5 Modifiez le script ci-dessus de telle manire que les trois bandes colores redeviennent noires
dans les cas o lutilisateur fournit une entre inacceptable.

Petit train : hritage, change dinformations entre objets


Dans lexercice prcdent, nous navons exploit quune seule caractristique des classes : lencapsulation. Celle-ci nous a permis dcrire un programme dans lequel les diffrentes fonctions (qui sont donc
devenues des mthodes) peuvent chacune accder un mme pool de variables : toutes celles qui sont
dfinies comme tant attaches self. Toutes ces variables peuvent tre considres en quelque sorte
comme des variables globales lintrieur de lobjet.
Comprenez bien toutefois quil ne sagit pas de vritables variables globales. Elles restent en effet
strictement confines lintrieur de lobjet, et il est dconseill de vouloir y accder de lextrieur79.
Dautre part, tous les objets que vous instancierez partir dune mme classe possderont chacun leur
propre jeu de ces variables, qui sont donc bel et bien encapsules dans ces objets. On les appelle pour
cette raison des attributs dinstance.
Nous allons prsent passer la vitesse suprieure, et raliser une petite application sur la base de
plusieurs classes, afin dexaminer comment diffrents objets peuvent schanger des informations par
lintermdiaire de leurs mthodes. Nous allons galement profiter de cet exercice pour vous montrer
comment vous pouvez dfinir la classe principale de votre application graphique par drivation dune
classe tkinter prexistante, mettant ainsi profit le mcanisme dhritage.
Le projet dvelopp ici est trs simple, mais il pourrait constituer une premire tape dans la
ralisation dun logiciel de jeu : nous en fournissons dailleurs des exemples plus loin (voir page 255). Il
sagit dune fentre contenant un canevas et deux boutons. Lorsque lon actionne le premier de ces
deux boutons, un petit train apparat dans le canevas. Lorsque lon actionne le second bouton,
quelques petits personnages apparaissent certaines fentres des wagons.
79 Comme

nous lavons dj signal prcdemment, Python vous permet d accder aux attributs dinstance
en utilisant la qualification des noms par points. D autres langages de programmation l interdisent, ou bien
ne lautorisent que moyennant une dclaration particulire de ces attributs (distinction entre attributs privs
et publics).
Sachez en tous cas que ce nest pas recommand : le bon usage de la programmation oriente objet stipule en
effet que vous ne devez pouvoir accder aux attributs des objets que par l intermdiaire de mthodes
spcifiques (linterface).

196

Classes et interfaces graphiques

Cahier des charges


Lapplication comportera deux classes :

La classe Application() sera obtenue par drivation dune des classes de base de tkinter : elle
mettra en place la fentre principale, son canevas et ses deux boutons.
Une classe Wagon(), indpendante, permettra dinstancier dans le canevas 4 objets-wagons
similaires, dots chacun dune mthode perso(). Celle-ci sera destine provoquer lapparition
dun petit personnage lune quelconque des trois fentres du wagon. Lapplication principale
invoquera cette mthode diffremment pour diffrents objets-wagons, afin de faire apparatre un
choix de quelques personnages.
Implmentation
1#
2#
3#
4#
5#
6#
7#
8#
9#
10#
11#
12#
13#
14#
15#
16#
17#
18#
19#
20#
21#
22#
23#
24#
25#
26#
27#
28#
29#
30#
31#
32#

from tkinter import *


def cercle(can, x, y, r):
"dessin d'un cercle de rayon <r> en <x,y> dans le canevas <can>"
can.create_oval(x-r, y-r, x+r, y+r)
class Application(Tk):
def __init__(self):
Tk.__init__(self)
# constructeur de la classe parente
self.can =Canvas(self, width =475, height =130, bg ="white")
self.can.pack(side =TOP, padx =5, pady =5)
Button(self, text ="Train", command =self.dessine).pack(side =LEFT)
Button(self, text ="Hello", command =self.coucou).pack(side =LEFT)
def dessine(self):
"instanciation de 4 wagons dans le canevas"
self.w1 = Wagon(self.can, 10, 30)
self.w2 = Wagon(self.can, 130, 30)
self.w3 = Wagon(self.can, 250, 30)
self.w4 = Wagon(self.can, 370, 30)
def coucou(self):
"apparition de personnages dans certaines fentres"
self.w1.perso(3)
# 1er wagon, 3e fentre
self.w3.perso(1)
# 3e wagon, 1e fentre
self.w3.perso(2)
# 3e wagon, 2e fentre
self.w4.perso(1)
# 4e wagon, 1e fentre
class Wagon(object):
def __init__(self, canev, x, y):
"dessin d'un petit wagon en <x,y> dans le canevas <canev>"
# mmorisation des paramtres dans des variables d'instance :

Petit train : hritage, change dinformations entre objets

197

33#
self.canev, self.x, self.y = canev, x, y
34#
# rectangle de base : 95x60 pixels :
35#
canev.create_rectangle(x, y, x+95, y+60)
36#
# 3 fentres de 25x40 pixels, cartes de 5 pixels :
37#
for xf in range(x+5, x+90, 30):
38#
canev.create_rectangle(xf, y+5, xf+25, y+40)
39#
# 2 roues de rayon gal 12 pixels :
40#
cercle(canev, x+18, y+73, 12)
41#
cercle(canev, x+77, y+73, 12)
42#
43#
def perso(self, fen):
44#
"apparition d'un petit personnage la fentre <fen>"
45#
# calcul des coordonnes du centre de chaque fentre :
46#
xf = self.x + fen*30 -12
47#
yf = self.y + 25
48#
cercle(self.canev, xf, yf, 10)
# visage
49#
cercle(self.canev, xf-5, yf-3, 2)
# oeil gauche
50#
cercle(self.canev, xf+5, yf-3, 2)
# oeil droit
51#
cercle(self.canev, xf, yf+5, 3)
# bouche
52#
53# app = Application()
54# app.mainloop()

Commentaires

Lignes 3 5 : Nous projetons de dessiner une srie de petits cercles. Cette petite fonction nous
facilitera le travail en nous permettant de dfinir ces cercles partir de leur centre et leur rayon.
Lignes 7 13 : La classe principale de notre application est construite par drivation de la classe de
fentres Tk() importe du module tkinter.80 Comme nous lavons expliqu au chapitre prcdent,
le constructeur dune classe drive doit activer lui-mme le constructeur de la classe parente, en
lui transmettant la rfrence de linstance comme premier argument.
Les lignes 10 13 servent mettre en place le canevas et les boutons.
Lignes 15 20 : Ces lignes instancient les 4 objets-wagons, produits partir de la classe
correspondante. Ceci pourrait tre programm plus lgamment laide dune boucle et dune
liste, mais nous le laissons ainsi pour ne pas alourdir inutilement les explications qui suivent.
Nous voulons placer nos objets-wagons dans le canevas, des emplacements bien prcis : il nous
faut donc transmettre quelques informations au constructeur de ces objets : au moins la rfrence
du canevas, ainsi que les coordonnes souhaites. Ces considrations nous font galement
entrevoir que lorsque nous dfinirons la classe Wagon(), un peu plus loin, nous devrons associer
sa mthode constructeur un nombre gal de paramtres afin de rceptionner ces arguments.
Lignes 22 27 : Cette mthode est invoque lorsque lon actionne le second bouton. Elle invoque
elle-mme la mthode perso() de certains objets-wagons, avec des arguments diffrents, afin de
faire apparatre les personnages aux fentres indiques. Ces quelques lignes de code vous
montrent donc comment un objet peut communiquer avec un autre, en faisant appel ses
mthodes. Il sagit-l du mcanisme central de la programmation par objets :
Les objets sont des entits programmes qui schangent des messages et interagissent
par lintermdiaire de leurs mthodes.
80 Nous

verrons plus loin que tkinter autorise galement de construire la fentre principale dune application
par drivation dune classe de widget (le plus souvent, il sagira dun widget Frame()). La fentre englobant ce
widget sera automatiquement ajoute (voir page 208).

198

Classes et interfaces graphiques

Idalement, la mthode coucou() devrait comporter quelques instructions complmentaires,


lesquelles vrifieraient dabord si les objets-wagons concerns existent bel et bien, avant dautoriser lactivation dune de leurs mthodes. Nous navons pas inclus ce genre de garde-fou afin que
lexemple reste aussi simple que possible, mais cela entrane la consquence que vous ne pouvez
pas actionner le second bouton avant le premier.
Lignes 29-30 : La classe Wagon() ne drive daucune autre classe prexistante. tant donn quil
sagit dune classe dobjets graphiques, nous devons cependant munir sa mthode constructeur de
paramtres, afin de recevoir la rfrence du canevas auquel les dessins sont destins, ainsi que les
coordonnes de dpart de ces dessins. Dans vos exprimentations ventuelles autour de cet
exercice, vous pourriez bien videmment ajouter encore dautres paramtres : taille du dessin,
orientation, couleur, vitesse, etc.
Lignes 31 51 : Ces instructions ne ncessitent gure de commentaires. La mthode perso() est
dote dun paramtre qui indique celle des 3 fentres o il faut faire apparatre un petit
personnage. Ici aussi nous navons pas prvu de garde-fou : vous pouvez invoquer cette mthode
avec un argument gal 4 ou 5, par exemple, ce qui produira des effets incorrects.
Lignes 53-54 : Pour cette application, contrairement la prcdente, nous avons prfr sparer la
cration de lobjet app, et son dmarrage par invocation de mainloop(), dans deux instructions
distinctes (en guise dexemple). Vous pourriez galement condenser ces deux instructions en une
seule, laquelle serait alors : Application().mainloop(), et faire ainsi lconomie dune variable.
Exercice
13.6 Perfectionnez le script dcrit ci-dessus, en ajoutant un paramtre couleur au constructeur de
la classe Wagon(), lequel dterminera la couleur de la cabine du wagon. Arrangez-vous
galement pour que les fentres soient noires au dpart, et les roues grises (pour raliser ce
dernier objectif, ajoutez aussi un paramtre couleur la fonction cercle()).
cette mme classe Wagon(), ajoutez encore une mthode allumer(), qui servira changer la
couleur des 3 fentres (initialement noires) en jaune, afin de simuler lallumage dun clairage
intrieur.
Ajoutez un bouton la fentre principale, qui puisse dclencher cet allumage. Profitez de
lamlioration de la fonction cercle() pour teinter le visage des petits personnages en rose
(pink), leurs yeux et leurs bouches en noir, et instanciez les objets-wagons avec des couleurs
diffrentes.

13.7 Ajoutez des correctifs au programme prcdent, afin que lon puisse utiliser nimporte quel
bouton dans le dsordre, sans que cela ne dclenche une erreur ou un effet bizarre.

OscilloGraphe : un widget personnalis


Le projet qui suit va nous entraner encore un petit peu plus loin. Nous allons y construire une nouvelle
classe de widget, quil sera possible dintgrer dans nos projets futurs comme nimporte quel widget
standard. Comme la classe principale de lexercice prcdent, cette nouvelle classe sera construite par
drivation dune classe tkinter prexistante.
Le sujet concret de cette application nous est inspir par le cours de physique.

OscilloGraphe : un widget personnalis

199

Pour rappel, un mouvement vibratoire harmonique se dfinit comme tant la projection dun
mouvement circulaire uniforme sur une droite. Les positions successives dun mobile qui effectue ce
type de mouvement sont traditionnellement repres par rapport une position centrale : on les
appelle alors des longations. Lquation qui dcrit lvolution de llongation dun tel mobile au cours
du temps est toujours de la forme e = A sin 2 f t , dans laquelle e reprsente llongation
du mobile tout instant t . Les constantes A, f et dsignent respectivement lamplitude, la frquence et
la phase du mouvement vibratoire.
Le but du prsent projet est de fournir un instrument de
visualisation simple de ces diffrents concepts, savoir
un systme daffichage automatique de graphiques
longation/temps. Lutilisateur pourra choisir librement
les valeurs des paramtres A, f et , et observer les
courbes qui en rsultent.
Le widget que nous allons construire dabord soccupera
de laffichage proprement dit. Nous construirons ensuite
dautres widgets pour faciliter lentre des paramtres
A, f et .
Veuillez donc encoder le script ci-dessous et le
sauvegarder dans un fichier, auquel vous donnerez le nom oscillo.py. Vous raliserez ainsi un vritable
module contenant une classe (vous pourrez par la suite ajouter dautres classes dans ce mme module, si
le cur vous en dit).
1# from tkinter import *
2# from math import sin, pi
3#
4# class OscilloGraphe(Canvas):
5#
"Canevas spcialis, pour dessiner des courbes longation/temps"
6#
def __init__(self, boss =None, larg=200, haut=150):
7#
"Constructeur du graphique : axes et chelle horiz."
8#
# construction du widget parent :
9#
Canvas.__init__(self)
# appel au constructeur
10#
self.configure(width=larg, height=haut)
# de la classe parente
11#
self.larg, self.haut = larg, haut
# mmorisation
12#
# trac des axes de rfrence :
13#
self.create_line(10, haut/2, larg, haut/2, arrow=LAST) # axe X
14#
self.create_line(10, haut-5, 10, 5, arrow=LAST)
# axe Y
15#
# trac d'une chelle avec 8 graduations :
16#
pas = (larg-25)/8.
# intervalles de l'chelle horizontale
17#
for t in range(1, 9):
18#
stx = 10 + t*pas
# +10 pour partir de l'origine
19#
self.create_line(stx, haut/2-4, stx, haut/2+4)
20#
21#
def traceCourbe(self, freq=1, phase=0, ampl=10, coul='red'):
22#
"trac d'un graphique longation/temps sur 1 seconde"
23#
curve =[]
# liste des coordonnes
24#
pas = (self.larg-25)/1000.
# l'chelle X correspond 1 seconde
25#
for t in range(0,1001,5):
# que l'on divise en 1000 ms.
26#
e = ampl*sin(2*pi*freq*t/1000 - phase)
27#
x = 10 + t*pas
28#
y = self.haut/2 - e*self.haut/25
29#
curve.append((x,y))

200

Classes et interfaces graphiques

30#
n = self.create_line(curve, fill=coul, smooth=1)
31#
return n
# n = numro d'ordre du trac
32#
33# #### Code pour tester la classe : ####
34#
35# if __name__ == '__main__':
36#
root = Tk()
37#
gra = OscilloGraphe(root, 250, 180)
38#
gra.pack()
39#
gra.configure(bg ='ivory', bd =2, relief=SUNKEN)
40#
gra.traceCourbe(2, 1.2, 10, 'purple')
41#
root.mainloop()

Le niveau principal du script est constitu par les lignes 35 41.


Comme nous lavons dj expliqu la page 188, les lignes de code situes aprs linstruction
if __name__ == '__main__': ne sont pas excutes si le script est import en tant que module dans
une autre application. Si on lance le script comme application principale, par contre, ces instructions
sexcutent. Nous disposons ainsi dun mcanisme intressant, qui nous permet dintgrer des
instructions de test lintrieur des modules, mme si ceux-ci sont destins tre imports dans
dautres scripts.
Lancez donc lexcution du script de la manire habituelle. Vous devriez obtenir un affichage similaire
celui qui est reproduit la page prcdente.
Exprimentation
Nous commenterons les lignes importantes du script un peu plus loin dans ce texte. Mais commenons
dabord par exprimenter quelque peu la classe que nous venons de construire.
Ouvrez donc votre terminal, et entrez les instructions ci-dessous directement la ligne de commande :
>>> from oscillo import *
>>> g1 = OscilloGraphe()
>>> g1.pack()

Aprs importation des classes du module oscillo, nous instancions un premier objet g1, de la classe
OscilloGraphe().
Puisque nous ne fournissons aucun argument, lobjet possde les dimensions par dfaut, dfinies dans
le constructeur de la classe. Remarquons au passage que nous navons mme pas pris la peine de
dfinir dabord une fentre matre pour y placer ensuite notre widget. tkinter nous pardonne cet oubli
et nous en fournit une automatiquement !
>>> g2 = OscilloGraphe(haut=200, larg=250)
>>> g2.pack()
>>> g2.traceCourbe()

Par ces instructions, nous crons un second widget de la mme classe, en prcisant cette fois ses
dimensions (hauteur et largeur, dans nimporte quel ordre).
Ensuite, nous activons la mthode traceCourbe() associe ce widget. tant donn que nous ne lui
fournissons aucun argument, la sinusode qui apparat correspond aux valeurs prvues par dfaut
pour les paramtres A, f et .
>>> g3 = OscilloGraphe(larg=220)
>>> g3.configure(bg='white', bd=3, relief=SUNKEN)

OscilloGraphe : un widget personnalis

201

>>> g3.pack(padx=5,pady=5)
>>> g3.traceCourbe(phase=1.57, coul='purple')
>>> g3.traceCourbe(phase=3.14, coul='dark green')

Pour comprendre la configuration de ce troisime widget, il


faut nous rappeler que la classe OscilloGraphe() a t
construite par drivation de la classe Canvas(). Elle hrite
donc toutes les proprits de celle-ci, ce qui nous permet de
choisir la couleur de fond, la bordure, etc., en utilisant les
mmes arguments que ceux qui sont notre disposition
lorsque nous configurons un canevas.
Nous faisons ensuite apparatre deux tracs successifs, en
faisant appel deux fois la mthode traceCourbe(), laquelle
nous fournissons des arguments pour la phase et la couleur.
Exercice
13.8 Crez un quatrime widget, de taille : 400 300,
couleur de fond : jaune, et faites-y apparatre
plusieurs courbes correspondant des frquences et
des amplitudes diffrentes.

Il est temps prsent que nous analysions la structure de la


classe qui nous a permis dinstancier tous ces widgets. Nous
avons donc enregistr cette classe dans le module oscillo.py
(voir page 199).
Cahier des charges
Nous souhaitons dfinir une nouvelle classe de widget,
capable dafficher automatiquement les graphiques
longation/temps correspondant divers mouvements
vibratoires harmoniques.
Ce widget doit pouvoir tre dimensionn volont au moment de son instanciation. Il doit faire
apparatre deux axes cartsiens X et Y munis de flches. Laxe X reprsentera lcoulement du temps
pendant une seconde au total, et il sera muni dune chelle comportant 8 intervalles.
Une mthode traceCourbe() sera associe ce widget. Elle provoquera le trac du graphique
longation/temps pour un mouvement vibratoire, dont on aura fourni la frquence (entre 0.25 et 10
Hz), la phase (entre 0 et 2 radians) et lamplitude (entre 1 et 10 ; chelle arbitraire).
Implmentation

Ligne 4 : La classe OscilloGraphe() est cre par drivation de la classe Canvas(). Elle hrite donc
toutes les proprits de celle-ci : on pourra configurer les objets de cette nouvelle classe en
utilisant les nombreuses options dj disponibles pour la classe Canvas().
Ligne 6 : La mthode constructeur utilise 3 paramtres, qui sont tous optionnels puisque chacun
dentre eux possde une valeur par dfaut. Le paramtre boss ne sert qu rceptionner la rf-

202

Classes et interfaces graphiques


rence dune fentre matresse ventuelle (voir exemples suivants). Les paramtres larg et haut
(largeur et hauteur) servent assigner des valeurs aux options width et height du canevas
parent, au moment de linstanciation.
Lignes 9-10 : Comme nous lavons dj dit plusieurs reprises, le constructeur dune classe drive
doit presque toujours commencer par activer le constructeur de sa classe parente. Nous ne
pouvons en effet hriter toute la fonctionnalit de la classe parente, que si cette fonctionnalit a
t effectivement mise en place et initialise.
Nous activons donc le constructeur de la classe Canvas() la ligne 9, et nous ajustons deux de ses
options la ligne 10. Notez au passage que nous pourrions condenser ces deux lignes en une seule,
qui deviendrait en loccurrence :
Canvas.__init__(self, width=larg, height=haut).
Et comme cela a galement dj t expliqu (cf. page 185), nous devons transmettre ce
constructeur la rfrence de linstance prsente (self) comme premier argument.
Ligne 11 : Il est ncessaire de mmoriser les paramtres larg et haut dans des variables dinstance,
parce que nous devrons pouvoir y accder aussi dans la mthode traceCourbe().
Lignes 13-14 : Pour tracer les axes X et Y, nous utilisons les paramtres larg et haut, ainsi ces axes
sont automatiquement mis dimension. Loption arrow=LAST permet de faire apparatre une
petite flche lextrmit de chaque ligne.
Lignes 16 19 : Pour tracer lchelle horizontale, on commence par rduire de 25 pixels la largeur
disponible, de manire mnager des espaces aux deux extrmits. On divise ensuite en 8
intervalles, que lon visualise sous la forme de 8 petits traits verticaux.
Ligne 21 : La mthode traceCourbe() pourra tre invoque avec quatre arguments. Chacun
dentre eux pourra ventuellement tre omis, puisque chacun des paramtres correspondants
possde une valeur par dfaut. Il sera galement possible de fournir les arguments dans nimporte
quel ordre, comme nous lavons dj expliqu la page 76.
Lignes 23 31 : Pour le trac de la courbe, la variable t prend successivement toutes les valeurs de
0 1000, et on calcule chaque fois llongation e correspondante, laide de la formule thorique
(ligne 26). Les couples de valeurs t et e ainsi trouves sont mises lchelle et transformes en
coordonnes x, y aux lignes 27 et 28, puis accumules dans la liste curve.
Lignes 30-31 : La mthode create_line() trace alors la courbe correspondante en une seule
opration, et elle renvoie le numro dordre du nouvel objet ainsi instanci dans le canevas (ce
numro dordre nous permettra dy accder encore par aprs : pour leffacer, par exemple). Loption smooth =1 amliore laspect final, par lissage.

Exercices
13.9 Modifiez le script de manire ce que laxe de rfrence vertical comporte lui aussi une
chelle, avec 5 tirets de part et dautre de lorigine.

13.10 Comme les widgets de la classe Canvas() dont il drive, votre widget peut intgrer des
indications textuelles. Il suffit pour cela dutiliser la mthode create_text(). Cette mthode
attend au moins trois arguments : les coordonnes x et y de lemplacement o vous voulez
faire apparatre votre texte et puis le texte lui-mme, bien entendu. Dautres arguments
peuvent tre transmis sous forme doptions, pour prciser par exemple la police de caractres

OscilloGraphe : un widget personnalis

203

et sa taille. Afin de voir comment cela fonctionne, ajoutez provisoirement la ligne suivante
dans le constructeur de la classe OscilloGraphe(), puis relancez le script :
self.create_text(130, 30, text = "Essai", anchor =CENTER)

Utilisez cette mthode pour ajouter au widget les indications suivantes aux extrmits des
axes de rfrence : e (pour longation ) le long de laxe vertical, et t (pour temps ) le long
de laxe horizontal. Le rsultat pourrait ressembler la figure de gauche.
13.11 Vous pouvez complter encore votre widget en y faisant apparatre une grille de rfrence
plutt que de simples tirets le long des axes. Pour viter que cette grille ne soit trop visible,
vous pouvez colorer ses traits en gris (option fill = grey), comme dans la figure de droite.
13.12 Compltez encore votre widget en y faisant apparatre des repres numriques.

Curseurs : un widget composite


Dans lexercice prcdent, vous avez construit un nouveau type de widget que vous avez sauvegard
dans le module oscillo.py. Conservez soigneusement ce module, car vous lintgrerez bientt dans un
projet plus complexe.
Pour linstant, vous allez construire un autre widget, plus interactif cette fois. Il sagira dune sorte de
panneau de contrle comportant trois curseurs de rglage et une case cocher. Comme le prcdent,
ce widget est destin tre rutilis dans une application de synthse.

Prsentation du widget Scale


Commenons dabord par dcouvrir un widget de
base, que nous navions pas encore utilis jusquici :
le widget Scale se prsente comme un curseur qui
coulisse devant une chelle. Il permet lutilisateur
de choisir rapidement la valeur dun paramtre
quelconque, dune manire trs attrayante.

204

Classes et interfaces graphiques

Le petit script ci-dessous vous montre comment le paramtrer et lutiliser dans une fentre :
from tkinter import *
def updateLabel(x):
lab.configure(text='Valeur actuelle = ' + str(x))
root = Tk()
Scale(root, length=250, orient=HORIZONTAL, label ='Rglage :',
troughcolor ='dark grey', sliderlength =20,
showvalue =0, from_=-25, to=125, tickinterval =25,
command=updateLabel).pack()
lab = Label(root)
lab.pack()
root.mainloop()

Ces lignes ne ncessitent gure de commentaires.


Vous pouvez crer des widgets Scale de nimporte quelle taille (option length), en orientation
horizontale (comme dans notre exemple) ou verticale (option orient = VERTICAL).
Les options from_ (attention : noubliez pas le caractre soulign , lequel est ncessaire afin dviter la confusion avec le mot rserv from !) et to dfinissent la plage de rglage. Lintervalle entre les
repres numriques est dfini dans loption tickinterval, etc.
La fonction dsigne dans loption command est appele automatiquement chaque fois que le curseur
est dplac, et la position actuelle du curseur par rapport lchelle lui est transmise en argument. Il
est donc trs facile dutiliser cette valeur pour effectuer un traitement quelconque. Considrez par
exemple le paramtre x de la fonction updateLabel(), dans notre exemple.
Le widget Scale constitue une interface trs intuitive et attrayante pour proposer diffrents rglages
aux utilisateurs de vos programmes. Nous allons prsent lincorporer en plusieurs exemplaires dans
une nouvelle classe de widget : un panneau de contrle destin choisir la frquence, la phase et lamplitude pour un mouvement vibratoire, dont nous afficherons ensuite le graphique longation/temps
laide du widget oscilloGraphe construit dans les pages prcdentes.

Construction dun panneau de contrle trois curseurs


Comme le prcdent, le script que nous dcrivons ci-dessous est destin tre sauvegard dans un
module, que vous nommerez cette fois curseurs.py. Les classes que vous sauvegardez ainsi seront
rutilises (par importation) dans une application de synthse que nous dcrirons un peu plus loin 81.
Nous attirons votre attention sur le fait que le code ci-dessous peut tre raccourci de diffrentes
manires (nous y reviendrons). Nous ne lavons pas optimis demble, parce que cela ncessiterait dy
incorporer un concept supplmentaire (les expressions lambda), ce que nous prfrons viter pour linstant.
Vous savez dj que les lignes de code places la fin du script permettent de tester son
fonctionnement. Vous devriez obtenir une fentre semblable celle-ci :

81 Vous

pourriez bien videmment aussi enregistrer plusieurs classes dans un mme module.

Curseurs : un widget composite

1#
2#
3#
4#
5#
6#
7#
8#
9#
10#
11#
12#
13#
14#
15#
16#
17#
18#
19#
20#
21#
22#
23#
24#
25#
26#
27#
28#
29#
30#
31#
32#
33#
34#
35#
36#
37#
38#
39#
40#
41#
42#
43#
44#
45#
46#
47#
48#
49#
50#
51#
52#
53#
54#

from tkinter import *


from math import pi
class ChoixVibra(Frame):
"""Curseurs pour choisir frquence, phase & amplitude d'une vibration"""
def __init__(self, boss =None, coul ='red'):
Frame.__init__(self)
# constructeur de la classe parente
# Initialisation de quelques attributs d'instance :
self.freq, self.phase, self.ampl, self.coul = 0, 0, 0, coul
# Variable d'tat de la case cocher :
self.chk = IntVar()
# 'objet-variable' tkinter
Checkbutton(self, text='Afficher', variable=self.chk,
fg = self.coul, command = self.setCurve).pack(side=LEFT)
# Dfinition des 3 widgets curseurs :
Scale(self, length=150, orient=HORIZONTAL, sliderlength =25,
label ='Frquence (Hz) :', from_=1., to=9., tickinterval =2,
resolution =0.25,
showvalue =0, command = self.setFrequency).pack(side=LEFT)
Scale(self, length=150, orient=HORIZONTAL, sliderlength =15,
label ='Phase (degrs) :', from_=-180, to=180, tickinterval =90,
showvalue =0, command = self.setPhase).pack(side=LEFT)
Scale(self, length=150, orient=HORIZONTAL, sliderlength =25,
label ='Amplitude :', from_=1, to=9, tickinterval =2,
showvalue =0, command = self.setAmplitude).pack(side=LEFT)
def setCurve(self):
self.event_generate('<Control-Z>')
def setFrequency(self, f):
self.freq = float(f)
self.event_generate('<Control-Z>')
def setPhase(self, p):
pp =float(p)
self.phase = pp*2*pi/360
# conversion degrs -> radians
self.event_generate('<Control-Z>')
def setAmplitude(self, a):
self.ampl = float(a)
self.event_generate('<Control-Z>')
#### Code pour tester la classe : ###
if __name__ == '__main__':
def afficherTout(event=None):
lab.configure(text = '{0} - {1} - {2} - {3}'.\
format(fra.chk.get(), fra.freq, fra.phase, fra.ampl))
root = Tk()
fra = ChoixVibra(root,'navy')
fra.pack(side =TOP)
lab = Label(root, text ='test')
lab.pack()
root.bind('<Control-Z>', afficherTout)
root.mainloop()

205

206

Classes et interfaces graphiques

Ce panneau de contrle permettra vos utilisateurs de rgler aisment la valeur des paramtres
indiqus (frquence, phase et amplitude), lesquels pourront alors servir commander laffichage de
graphiques longation/temps dans un widget de la classe OscilloGraphe() construite prcdemment,
comme nous le montrerons dans lapplication de synthse.
Commentaires

Ligne 6 : La mthode constructeur utilise un paramtre optionnel coul. Ce paramtre


permettra de choisir une couleur pour le graphique soumis au contrle du widget. Le paramtre
boss sert rceptionner la rfrence dune fentre matresse ventuelle (voir plus loin).
Ligne 7 : Activation du constructeur de la classe parente (pour hriter sa fonctionnalit).
Ligne 9 : Dclaration de quelques variables dinstance. Leurs vraies valeurs seront dtermines par
les mthodes des lignes 29 40 (gestionnaires dvnements).
Ligne 11 : Cette instruction instancie un objet de la classe IntVar(), laquelle fait partie du module
tkinter au mme titre que les classes similaires DoubleVar(), StringVar() et BooleanVar().
Toutes ces classes permettent de dfinir des variables tkinter, lesquels sont en fait des objets, mais
qui se comportent comme des variables lintrieur des widgets tkinter (voir ci-aprs).
Ainsi lobjet rfrenc dans self.chk contient lquivalent dune variable de type entier, dans un
format utilisable par tkinter. Pour accder sa valeur depuis Python, il faut utiliser des mthodes
spcifiques de cette classe dobjets : la mthode set() permet de lui assigner une valeur, et la
mthode get() permet de la rcuprer (ce que lon mettra en pratique la ligne 47).
Ligne 12 : Loption variable de lobjet checkbutton est associe la variable tkinter dfinie la
ligne prcdente. Nous ne pouvons pas rfrencer directement une variable ordinaire dans la
dfinition dun widget tkinter, parce que tkinter lui-mme est crit dans un langage qui nutilise
pas les mmes conventions que Python pour formater ses variables. Les objets construits partir
des classes de variables tkinter sont donc ncessaires pour assurer linterface.
Ligne 13 : Loption command dsigne la mthode que le systme doit invoquer lorsque lutilisateur effectue un clic de souris dans la case cocher.
Lignes 14 24 : Ces lignes dfinissent les trois widgets curseurs, en trois instructions similaires. Il
serait plus lgant de programmer tout ceci en une seule instruction, rpte trois fois laide
dune boucle. Cela ncessiterait cependant de faire appel un concept que nous navons pas
encore expliqu (les fonctions ou expressions lamdba), et la dfinition du gestionnaire dvnements
associ ces widgets deviendrait elle aussi plus complexe. Conservons donc pour cette fois des
instructions spares : nous nous efforcerons damliorer tout cela plus tard.
Lignes 26 40 : Les 4 widgets dfinis dans les lignes prcdentes possdent chacun une option
command. Pour chacun deux, la mthode invoque dans cette option command est diffrente :
la case cocher active la mthode setCurve(), le premier curseur active la mthode setFrequency(), le second curseur active la mthode setPhase(), et le troisime curseur active la mthode setAmplitude(). Remarquez bien au passage que loption command des widgets Scale transmet un
argument la mthode associe (la position actuelle du curseur), alors que la mme option command ne transmet rien dans le cas du widget Checkbutton.
Ces 4 mthodes (qui sont donc les gestionnaires des vnements produits par la case cocher et
a

Curseurs : un widget composite

207

les trois curseurs) provoquent elles-mmes chacune lmission dun nouvel vnement82, en faisant
appel la mthode event_generate().
Lorsque cette mthode est invoque, Python envoie au systme dexploitation exactement le
mme message-vnement que celui qui se produirait si lutilisateur enfonait simultanment les
touches <Ctrl>, <Maj> et <Z> de son clavier.
Nous produisons ainsi un message-vnement bien particulier, qui peut tre dtect et trait par
un gestionnaire dvnement associ un autre widget (voir page suivante). De cette manire,
nous mettons en place un vritable systme de communication entre widgets : chaque fois que
lutilisateur exerce une action sur notre panneau de contrle, celui-ci gnre un vnement
spcifique, qui signale cette action lattention des autres widgets prsents.
a

Nous aurions pu choisir une autre combinaison de touches (ou mme carrment un autre
type dvnement). Notre choix sest port sur celle-ci parce quil y a vraiment trs peu de
chances que lutilisateur sen serve alors quil examine notre programme. Nous pourrons
cependant produire nous-mmes un tel vnement au clavier titre de test, lorsque le
moment sera venu de vrifier le gestionnaire de cet vnement, que nous mettrons en
place par ailleurs.

Lignes 42 54 : Comme nous lavions dj fait pour oscillo.py, nous compltons ce nouveau
module par quelques lignes de code au niveau principal. Ces lignes permettent de tester le bon
fonctionnement de la classe : elles ne sexcutent que si on lance le module directement, comme
une application part entire. Veillez utiliser vous-mme cette technique dans vos propres
modules, car elle constitue une bonne pratique de programmation : lutilisateur de modules
construits ainsi peut en effet (re)dcouvrir trs aisment leur fonctionnalit (en les excutant) et
la manire de sen servir (en analysant ces quelques lignes de code).
Dans ces lignes de test, nous construisons une fentre principale root qui contient deux widgets :
un widget de la nouvelle classe ChoixVibra() et un widget de la classe Label().
la ligne 53, nous associons la fentre principale un gestionnaire dvnement : tout vnement
du type spcifi dclenche dsormais un appel de la fonction afficherTout().
Cette fonction est donc notre gestionnaire dvnement spcialis, qui est sollicit chaque fois
quun vnement de type <Maj-Ctrl-Z> est dtect par le systme dexploitation.
Comme nous lavons dj expliqu plus haut, nous avons fait en sorte que de tels vnements
soient produits par les objets de la classe ChoixVibra(), chaque fois que lutilisateur modifie ltat
de lun ou lautre des trois curseurs, ou celui de la case cocher.
Conue seulement pour effectuer un test, la fonction afficherTout() ne fait rien dautre que
provoquer laffichage des valeurs des variables associes chacun de nos quatre widgets, en
(re)configurant loption text dun widget de classe Label().
Ligne 47, expression fra.chk.get() : nous avons vu plus haut que la variable mmorisant ltat de
la case cocher est un objet-variable tkinter. Python ne peut pas lire directement le contenu
dune telle variable, qui est en ralit un objet-interface. Pour en extraire la valeur, il faut donc
faire usage dune mthode spcifique de cette classe dobjets : la mthode get().
a

82 En

fait, on devrait plutt appeler cela un message (qui est lui-mme la notification d un vnement).
Veuillez relire ce sujet les explications de la page 83 : Programmes pilots par des vnements.

208

Classes et interfaces graphiques

Propagation des vnements


Le mcanisme de communication dcrit ci-dessus respecte la hirarchie de classes des widgets. Vous
aurez not que la mthode qui dclenche lvnement est associe au widget dont nous sommes en
train de dfinir la classe, par lintermdiaire de self. En gnral, un message-vnement est en effet
associ un widget particulier (par exemple, un clic de souris sur un bouton est associ ce bouton),
ce qui signifie que le systme dexploitation va dabord examiner sil existe un gestionnaire pour ce
type dvnement, qui soit lui aussi associ ce widget. Sil en existe un, cest celui-l qui est activ, et
la propagation du message sarrte. Sinon, le message-vnement est prsent successivement aux
widgets matres, dans lordre hirarchique, jusqu ce quun gestionnaire dvnement soit trouv, ou
bien jusqu ce que la fentre principale soit atteinte.
Les vnements correspondant des frappes sur le clavier (telle la combinaison de touches <Maj-CtrlZ> utilise dans notre exercice) sont cependant toujours expdis directement la fentre principale
de lapplication. Dans notre exemple, le gestionnaire de cet vnement doit donc tre associ la fentre
root.
Exercices
13.13 Votre nouveau widget hrite des proprits de la classe Frame(). Vous pouvez donc modifier
son aspect en modifiant les options par dfaut de cette classe, laide de la mthode configure(). Essayez par exemple de faire en sorte que le panneau de contrle soit entour dune
bordure de 4 pixels ayant laspect dun sillon (bd = 4, relief = GROOVE). Si vous ne
comprenez pas bien ce quil faut faire, inspirez-vous du script oscillo.py (ligne 10).

13.14 Si lon assigne la valeur 1 loption showvalue des widgets Scale(), la position prcise du
curseur par rapport lchelle est affiche en permanence. Activez donc cette fonctionnalit
pour le curseur qui contrle le paramtre phase .
13.15 Loption troughcolor des widgets Scale() permet de dfinir la couleur de leur glissire.
Utilisez cette option pour faire en sorte que la couleur des glissires des 3 curseurs soit celle
qui est utilise comme paramtre lors de linstanciation de votre nouveau widget.
13.16 Modifiez le script de telle manire que les widgets curseurs soient carts davantage les uns
des autres (options padx et pady de la mthode pack()).

Intgration de widgets composites dans une application synthse


Dans les exercices prcdents, nous avons construit deux nouvelles classes de widgets : le widget OscilloGraphe(), canevas spcialis pour le dessin de sinusodes, et le widget ChoixVibra(), panneau de
contrle trois curseurs permettant de choisir les paramtres dune vibration.
Ces widgets sont dsormais disponibles dans les modules oscillo.py et curseurs.py83.
Nous allons prsent les utiliser dans une application synthse : un widget OscilloGraphe() y affiche
un, deux, ou trois graphiques superposs, de couleurs diffrentes, chacun dentre eux tant soumis au
contrle dun widget ChoixVibra().
83 Il

va de soit que nous pourrions aussi rassembler toutes les classes que nous construisons dans un seul
module.

Intgration de widgets composites dans une application synthse

209

Le script correspondant est reproduit ci-aprs.


Nous attirons votre attention sur la technique mise en uvre pour provoquer un rafrachissement de
laffichage dans le canevas par lintermdiaire dun vnement, chaque fois que lutilisateur effectue
une action quelconque au niveau de lun des panneaux de contrle.
Rappelez-vous que les applications destines fonctionner dans une interface graphique doivent tre
conues comme des programmes pilots par les vnements (voir page 83).
En prparant cet exemple, nous avons arbitrairement dcid que laffichage des graphiques serait
dclench par un vnement particulier, tout fait similaire ceux que gnre le systme dexploitation lorsque lutilisateur accomplit une action quelconque. Dans la gamme (trs tendue) dvnements possibles, nous en avons choisi un qui ne risque gure dtre utilis pour dautres raisons,
pendant que notre application fonctionne : la combinaison de touches <Maj-Ctrl-Z>.
Lorsque nous avons construit la classe de widgets ChoixVibra(), nous y avons donc incorpor les
instructions ncessaires pour que de tels vnements soient gnrs chaque fois que lutilisateur
actionne lun des curseurs ou modifie ltat de la case cocher. Nous allons prsent dfinir le
gestionnaire de cet vnement et linclure dans notre nouvelle classe : nous lappellerons montreCourbes() et il se chargera de rafrachir laffichage. tant donn que lvnement concern est du type
<enfoncement dune touche>, nous devrons cependant le dtecter au niveau de la fentre principale
de lapplication.

210
1#
2#
3#
4#
5#
6#
7#
8#
9#
10#
11#
12#
13#
14#
15#
16#
17#
18#
19#
20#
21#
22#
23#
24#
25#
26#
27#
28#
29#
30#
31#
32#
33#
34#
35#
36#
37#
38#
39#
40#
41#
42#
43#
44#
45#

Classes et interfaces graphiques


from oscillo import *
from curseurs import *
class ShowVibra(Frame):
"""Dmonstration de mouvements vibratoires harmoniques"""
def __init__(self, boss =None):
Frame.__init__(self)
# constructeur de la classe parente
self.couleur = ['dark green', 'red', 'purple']
self.trace = [0]*3
# liste des tracs (courbes dessiner)
self.controle = [0]*3
# liste des panneaux de contrle
# Instanciation du canevas avec axes X et Y :
self.gra = OscilloGraphe(self, larg =400, haut=200)
self.gra.configure(bg ='white', bd=2, relief=SOLID)
self.gra.pack(side =TOP, pady=5)
# Instanciation de 3 panneaux de contrle (curseurs) :
for i in range(3):
self.controle[i] = ChoixVibra(self, self.couleur[i])
self.controle[i].pack()
# Dsignation de l'vnement qui dclenche l'affichage des tracs :
self.master.bind('<Control-Z>', self.montreCourbes)
self.master.title('Mouvements vibratoires harmoniques')
self.pack()
def montreCourbes(self, event):
"""(R)Affichage des trois graphiques longation/temps"""
for i in range(3):
# D'abord, effacer le trac prcdent (ventuel) :
self.gra.delete(self.trace[i])
# Ensuite, dessiner le nouveau trac :
if self.controle[i].chk.get():
self.trace[i] = self.gra.traceCourbe(
coul = self.couleur[i],
freq = self.controle[i].freq,
phase = self.controle[i].phase,
ampl = self.controle[i].ampl)
#### Code pour tester la classe : ###
if __name__ == '__main__':
ShowVibra().mainloop()

Commentaires

Lignes 1-2 : Nous pouvons nous passer dimporter le module tkinter : chacun de ces deux modules
sen charge dj.
Ligne 4 : Puisque nous commenons connatre les bonnes techniques, nous dcidons de
construire lapplication elle-mme sous la forme dune nouvelle classe de widget, drive de la
classe Frame() : ainsi nous pourrons plus tard lintgrer toute entire dans dautres projets, si le
cur nous en dit.
Lignes 8-10 : Dfinition de quelques variables dinstance (3 listes) : les trois courbes traces seront
des objets graphiques, dont les couleurs sont prdfinies dans la liste self.couleur ; nous devons

Intgration de widgets composites dans une application synthse

211

prparer galement une liste self.trace pour mmoriser les rfrences de ces objets graphiques, et
enfin une liste self.controle pour mmoriser les rfrences des trois panneaux de contrle.
Lignes 13 15 : Instanciation du widget daffichage. tant donn que la classe OscilloGraphe() a
t obtenue par drivation de la classe Canvas(), il est toujours possible de configurer ce widget
en redfinissant les options spcifiques de cette classe (ligne 13).
Lignes 18 20 : Pour instancier les trois widgets panneau de contrle , on utilise une boucle.
Leurs rfrences sont mmorises dans la liste self.controle prpare la ligne 10. Ces panneaux
de contrle sont instancis comme esclaves du prsent widget, par lintermdiaire du paramtre
self. Un second paramtre leur transmet la couleur du trac contrler.
Lignes 23-24 : Au moment de son instanciation, chaque widget tkinter reoit automatiquement un
attribut master qui contient la rfrence de la fentre principale de lapplication. Cet attribut se
rvle particulirement utile si la fentre principale a t instancie implicitement par tkinter,
comme cest le cas ici.
Rappelons en effet que lorsque nous dmarrons une application en instanciant directement un
widget tel que Frame(), par exemple (cest ce que nous avons fait la ligne 4), tkinter instancie
automatiquement une fentre matresse pour ce widget (un objet de la classe Tk()).
Comme cet objet a t cr automatiquement, nous ne disposons daucune rfrence dans notre
code pour y accder, si ce nest par lintermdiaire de cet attribut master que tkinter associe
automatiquement chaque widget. Nous nous servons de cette rfrence pour redfinir le
bandeau-titre de la fentre principale ( la ligne 24), et pour y attacher un gestionnaire dvnement
( la ligne 23).
Lignes 27 40 : La mthode dcrite ici est le gestionnaire des vnements <Maj-Ctrl-Z>
spcifiquement dclenchs par nos widgets ChoixVibra() (ou panneaux de contrle ), chaque
fois que lutilisateur exerce une action sur un curseur ou une case cocher. Dans tous les cas, les
graphiques ventuellement prsents sont dabord effacs (ligne 28) laide de la mthode
delete() : le widget OscilloGraphe() a hrit cette mthode de sa classe parente Canvas().
Ensuite, de nouvelles courbes sont retraces, pour chacun des panneaux de contrle dont on a
coch la case Afficher . Chacun des objets ainsi dessins dans le canevas possde un numro de
rfrence, renvoy par la mthode traceCourbe() de notre widget OscilloGraphe().
Les numros de rfrence de nos dessins sont mmoriss dans la liste self.trace. Ils permettent
deffacer individuellement chacun dentre eux (cf. instruction de la ligne 28).
Lignes 38-40 : Les valeurs de frquence, phase et amplitude que lon transmet la mthode traceCourbe() sont les attributs dinstance correspondants de chacun des trois panneaux de contrle,
eux-mmes mmoriss dans la liste self.controle. Nous pouvons rcuprer ces attributs en
utilisant la qualification des noms par points.

212

Classes et interfaces graphiques

Exercices
13.17 Modifiez le script, de manire obtenir laspect ci-dessous (cran daffichage avec grille de
rfrence, panneaux de contrle entours dun sillon) :

13.18 Modifiez le script, de manire faire apparatre et contrler 4 graphiques au lieu de trois. Pour
la couleur du quatrime graphique, choisissez par exemple : blue, navy, maroon...
13.19 Aux lignes 33-35, nous rcuprons les valeurs des frquence, phase et amplitude choisies par
lutilisateur sur chacun des trois panneaux de contrle, en accdant directement aux attributs
dinstance correspondants. Python autorise ce raccourci et cest bien pratique mais cette
technique est dangereuse. Elle enfreint lune des recommandations de la thorie gnrale de la
programmation oriente objet , qui prconise que laccs aux proprits des objets soit
toujours pris en charge par des mthodes spcifiques. Pour respecter cette recommandation,
ajoutez la classe ChoixVibra() une mthode supplmentaire que vous appellerez valeurs(),
et qui renverra un tuple contenant les valeurs de la frquence, la phase et lamplitude choisies.
Les lignes 33 35 du prsent script pourront alors tre remplaces par quelque chose comme :
freq, phase, ampl = self.control[i].valeurs()

13.20 crivez une petite application qui fait apparatre une fentre avec un canevas et un widget
curseur (Scale). Dans le canevas, dessinez un cercle, dont lutilisateur pourra faire varier la
taille laide du curseur.
13.21 crivez un script qui crera deux classes : une classe Application, drive de Frame(), dont le
constructeur instanciera un canevas de 400 400 pixels, ainsi que deux boutons. Dans le
canevas, vous instancierez un objet de la classe Visage dcrite ci-aprs.

Intgration de widgets composites dans une application synthse

213

La classe Visage servira dfinir des objets graphiques censs reprsenter des visages
humains simplifis. Ces visages seront constitus dun cercle principal dans lequel trois ovales
plus petits reprsenteront deux yeux et une bouche (ouverte). Une mthode fermer
permettra de remplacer lovale de la bouche par une ligne horizontale. Une mthode ouvrir permettra de restituer la bouche de forme ovale.
Les deux boutons dfinis dans la classe Application serviront respectivement fermer et
ouvrir la bouche de lobjet Visage install dans le canevas. Vous pouvez vous inspirer de
lexemple de la page 88 pour composer une partie du code.
13.22 Exercice de synthse : laboration dun dictionnaire de couleurs.
But : raliser un petit programme utilitaire, qui puisse vous aider construire facilement et
rapidement un nouveau dictionnaire de couleurs, lequel permettrait laccs technique une
couleur quelconque par lintermdiaire de son nom usuel en franais.
Contexte : en manipulant divers objets colors avec tkinter, vous avez constat que cette
bibliothque graphique accepte quon lui dsigne les couleurs les plus fondamentales sous la
forme de chanes de caractres contenant leur nom en anglais : red, blue, yellow, etc.
Vous savez cependant quun ordinateur ne peut traiter que des informations numrises. Cela
implique que la dsignation dune couleur quelconque doit ncessairement tt ou tard tre
encode sous la forme dun nombre. Il faut bien entendu adopter pour cela une une
convention, et celle-ci peut varier dun systme un autre. Lune de ces conventions, parmi les
plus courantes, consiste reprsenter une couleur laide de trois octets, qui indiqueront
respectivement les intensits des trois composantes rouge, verte et bleue de cette couleur.
Cette convention peut tre utilise avec tkinter pour accder nimporte quelle nuance
colore. Vous pouvez en effet lui indiquer la couleur dun lment graphique quelconque,
laide dune chane de 7 caractres telle que #00FA4E. Dans cette chane, le premier
caractre (#) signifie que ce qui suit est une valeur hexadcimale. Les six caractres suivants
reprsentent les 3 valeurs hexadcimales des 3 composantes rouge, vert et bleu.
Pour visualiser concrtement la correspondance entre une couleur quelconque et son code,
vous pouvez explorer les ressources de divers programmes de traitement dimages, tels les
excellents programmes libres Gimp et Inkscape, par exemple.
tant donn quil nest pas facile pour les humains que nous sommes de mmoriser de tels
codes hexadcimaux, tkinter est galement dot dun dictionnaire de conversion, qui autorise
lutilisation de noms communs pour un certain nombre de couleurs parmi les plus courantes,
mais cela ne marche que pour des noms de couleurs exprims en anglais. Le but du prsent
exercice est de raliser un logiciel qui facilitera la construction dun dictionnaire quivalent
en franais, lequel pourrait ensuite tre incorpor lun ou lautre de vos propres
programmes. Une fois construit, ce dictionnaire serait donc de la forme :
{'vert':'#00FF00', 'bleu':'#0000FF', ... etc ...}.
Cahier des charges :
Lapplication raliser sera une application graphique, construite autour dune classe. Elle
comportera une fentre avec un certain nombre de champs dentre et de boutons, afin que
lutilisateur puisse aisment encoder de nouvelles couleurs en indiquant chaque fois son

214

Classes et interfaces graphiques


nom franais dans un champ, et son code hexadcimal dans un autre.
Lorsque le dictionnaire contiendra dj un certain nombre de donnes, il devra tre possible
de le tester, cest--dire dentrer un nom de couleur en franais et de retrouver le code
hexadcimal correspondant laide dun bouton (avec affichage ventuel dune zone colore).
Un bouton provoquera lenregistrement du dictionnaire dans un fichier texte. Un autre
permettra de reconstruire le dictionnaire partir du fichier.

13.23 Le script ci-dessous correspond une bauche de projet dessinant des ensembles de ds
jouer disposs lcran de plusieurs manires diffrentes (cette bauche pourrait tre une
premire tape dans la ralisation dun logiciel de jeu). Lexercice consistera analyser ce
script et le complter. Vous vous placerez ainsi dans la situation dun programmeur charg
de continuer le travail commenc par quelquun dautre, ou encore dans celle de linformaticien pri de participer un travail dquipe.
A) Commencez par analyser ce script et ajoutez-y des commentaires, en particulier aux lignes
marques : #***, pour montrer que vous comprenez ce que doit faire le programme ces
emplacements :
from tkinter import *
class FaceDom(object):
def __init__(self, can, val, pos, taille
self.can =can
# ***
x, y, c = pos[0], pos[1], taille/2
can.create_rectangle(x -c, y-c, x+c,
d = taille/3
# ***
self.pList =[]
# ***
pDispo = [((0,0),), ((-d,d),(d,-d)),
disp = pDispo[val -1]
# ***
for p in disp:
self.cercle(x +p[0], y +p[1], 5,

=70):

y+c, fill ='ivory', width =2)

((-d,-d), (0,0), (d,d))]

'red')

def cercle(self, x, y, r, coul):


# ***
self.pList.append(self.can.create_oval(x-r, y-r, x+r, y+r, fill=coul))
def effacer(self):
# ***
for p in self.pList:
self.can.delete(p)
class Projet(Frame):
def __init__(self, larg, haut):
Frame.__init__(self)
self.larg, self.haut = larg, haut
self.can = Canvas(self, bg='dark green', width =larg, height =haut)
self.can.pack(padx =5, pady =5)
# ***
bList = [("A", self.boutA), ("B", self.boutB),
("C", self.boutC), ("D", self.boutD),
("Quitter", self.boutQuit)]
for b in bList:
Button(self, text =b[0], command =b[1]).pack(side =LEFT)
self.pack()

Intgration de widgets composites dans une application synthse

215

def boutA(self):
self.d3 = FaceDom(self.can, 3, (100,100), 50)
def boutB(self):
self.d2 = FaceDom(self.can, 2, (200,100), 80)
def boutC(self):
self.d1 = FaceDom(self.can, 1, (350,100), 110)
def boutD(self):
# ***
self.d3.effacer()
def boutQuit(self):
self.master.destroy()
Projet(500, 300).mainloop()

B) Modifiez ensuite ce script, afin quil corresponde au cahier des charges suivant :
Le canevas devra tre plus grand : 600 600 pixels.
Les boutons de commande devront tre dplacs droite et espacs davantage.
La taille des points sur une face de d devra varier proportionnellement la taille de cette
face.
Variante 1 :
Ne conservez que les 2 boutons <A> et <B>. Chaque utilisation du bouton <A> fera apparatre 3
nouveaux ds (de mme taille, plutt petits) disposs sur une colonne (verticale), les valeurs
de ces ds tant tires au hasard entre 1 et 6. Chaque nouvelle colonne sera dispose la droite
de la prcdente. Si lun des tirages de 3 ds correspond 4, 2, 1 (dans nimporte quel ordre),
un message gagn sera affich dans la fentre (ou dans le canevas). Le bouton <B>
provoquera leffacement complet (pas seulement les points !) de tous les ds affichs.
Variante 2 :
Ne conservez que les 2 boutons <A> et <B>. Le bouton <A> fera apparatre 5 ds disposs en
quinconce (cest--dire comme les points dune face de valeur 5). Les valeurs de ces ds seront
tires au hasard entre 1 et 6, mais il ne pourra pas y avoir de doublons. Le bouton <B>
provoquera leffacement complet (pas seulement les points !) de tous les ds affichs.
Variante 3 :
Ne conservez que les 3 boutons <A>, <B> et <C>. Le bouton <A> fera apparatre 13 ds de mme
taille disposs en cercle. Chaque utilisation du bouton <B> provoquera un changement de
valeur du premier d, puis du deuxime, du troisime, etc. La nouvelle valeur dun d sera
chaque fois gale a sa valeur prcdente augmente dune unit, sauf dans le cas ou la valeur
prcdente tait 6 : dans ce cas la nouvelle valeur est 1, et ainsi de suite. Le bouton <C>
provoquera leffacement complet (pas seulement les points !) de tous les ds affichs.
Variante 4 :
Ne conservez que les 3 boutons <A>, <B> et <C>. Le bouton <A> fera apparatre 12 ds de mme
taille disposs sur deux lignes de 6. Les valeurs des ds de la premire ligne seront dans lordre

216

Classes et interfaces graphiques


1, 2, 3, 4, 5, 6. Les valeurs des ds de la seconde ligne seront tires au hasard entre 1 et 6.
Chaque utilisation du bouton <B> provoquera un changement de valeur alatoire du premier
d de la seconde ligne, tant que cette valeur restera diffrente de celle du d correspondant
dans la premire ligne.
Lorsque le 1er d de la 2e ligne aura acquis la valeur de son correspondant, cest la valeur du 2e
d de la seconde ligne qui sera change au hasard, et ainsi de suite, jusqu ce que les 6 faces du
bas soient identiques celles du haut. Le bouton <C> provoquera leffacement complet (pas
seulement les points !) de tous les ds affichs.

14
Et pour quelques widgets de
plus...

14

Pour vous aider baucher vos propres projets personnels, nous vous prsentons ici quelques
nouveaux widgets, ainsi que des utilisations avances de ceux que vous connaissez dj. Nous ne
prtendons toutefois pas difier ainsi une documentation de rfrence sur tkinter : vous la
trouverez plutt dans les ouvrages ou les sites web spcialiss. Mais attention : au-del de leur
vise documentaire, les pages qui suivent sont galement destines vous apprendre par lexemple
comment sarticule une application construite laide de classes et dobjets. Vous y dcouvrirez
dailleurs au passage quelques techniques Python qui nont pas encore t abordes auparavant,
comme les expressions lambda ou le paramtrage implicite des fonctions.

Les boutons radio


Les widgets boutons radio permettent de proposer lutilisateur un ensemble de choix
mutuellement exclusifs. On les appelle ainsi par analogie avec les boutons de slection que lon
trouvait jadis sur les postes de radio. Ces boutons taient conus de telle manire quun seul la fois
pouvait tre enfonc : tous les autres ressortaient automatiquement.
La caractristique essentielle de ces widgets
est quon les utilise toujours par groupes.
Tous les boutons radio faisant partie dun
mme groupe sont associs une seule et
mme variable tkinter, mais chacun dentre
eux se voit aussi attribuer une valeur
particulire.
Lorsque lutilisateur slectionne lun des boutons, la valeur correspondant ce bouton est affecte la
variable tkinter commune.
1# from tkinter import *
2#
3# class RadioDemo(Frame):
4#
"""Dmo : utilisation de widgets 'boutons radio'"""

218

Et pour quelques widgets de plus...

5#
def __init__(self, boss =None):
6#
"""Cration d'un champ d'entre avec 4 boutons radio"""
7#
Frame.__init__(self)
8#
self.pack()
9#
# Champ d'entre contenant un petit texte :
10#
self.texte = Entry(self, width =30, font ="Arial 14")
11#
self.texte.insert(END, "La programmation, c'est gnial")
12#
self.texte.pack(padx =8, pady =8)
13#
# Nom franais et nom technique des quatre styles de police :
14#
stylePoliceFr =["Normal", "Gras", "Italique", "Gras/Italique"]
15#
stylePoliceTk =["normal", "bold", "italic" , "bold italic"]
16#
# Le style actuel est mmoris dans un 'objet-variable' tkinter ;
17#
self.choixPolice = StringVar()
18#
self.choixPolice.set(stylePoliceTk[0])
19#
# Cration des quatre 'boutons radio' :
20#
for n in range(4):
21#
bout = Radiobutton(self,
22#
text = stylePoliceFr[n],
23#
variable = self.choixPolice,
24#
value = stylePoliceTk[n],
25#
command = self.changePolice)
26#
bout.pack(side =LEFT, padx =5)
27#
28#
def changePolice(self):
29#
"""Remplacement du style de la police actuelle"""
30#
police = "Arial 15 " + self.choixPolice.get()
31#
self.texte.configure(font =police)
32#
33# if __name__ == '__main__':
34#
RadioDemo().mainloop()

Commentaires
Ligne 3 : Cette fois encore, nous prfrons construire notre petite application comme une classe
drive de la classe Frame(), ce qui nous permettrait ventuellement de lintgrer sans difficult
dans une application plus importante.
Ligne 8 : En gnral, on applique les mthodes de positionnement des widgets ( pack(), grid(), ou
place()) aprs instanciation de ceux-ci, ce qui permet de choisir librement leur disposition lintrieur des fentres matresses. Comme nous le montrons ici, il est cependant tout fait possible
de dj prvoir ce positionnement dans le constructeur du widget.
Ligne 11 : Les widgets de la classe Entry disposent de plusieurs mthodes pour accder la chane
de caractres affiche. La mthode get() permet de rcuprer la chane entire. La mthode insert() permet dinsrer de nouveaux caractres un emplacement quelconque (cest--dire au
dbut, la fin, ou mme lintrieur dune chane prexistante ventuelle). Cette mthode sutilise donc avec deux arguments, le premier indiquant lemplacement de linsertion (utilisez 0 pour
insrer au dbut, END pour insrer la fin, ou encore un indice numrique quelconque pour
dsigner un caractre dans la chane). La mthode delete() permet deffacer tout ou partie de la
chane. Elle sutilise avec les mmes arguments que la prcdente (cf. projet Code des couleurs ,
page 191).
Lignes 14-15 : Plutt que de les instancier dans des instructions spares, nous prfrons crer nos
quatre boutons laide dune boucle. Les options spcifiques chacun deux sont dabord
prpares dans les deux listes stylePoliceFr et stylePoliceTk : la premire contient les petits

Les boutons radio

219

textes qui devront safficher en regard de chaque bouton, et la seconde les valeurs qui devront
leur tre associes.
Lignes 17-18 : Comme expliqu la page prcdente, les quatre boutons forment un groupe autour
dune variable commune. Cette variable prendra la valeur associe au bouton radio que lutilisateur dcidera de choisir. Nous ne pouvons cependant pas utiliser une variable ordinaire pour
remplir ce rle, parce que les attributs internes des objets tkinter ne sont accessibles quau travers
de mthodes spcifiques. Une fois de plus, nous utilisons donc ici un objet-variable tkinter, de type
chane de caractres, que nous instancions partir de la classe StringVar(), et auquel nous
donnons une valeur par dfaut la ligne 18.
Lignes 20 26 : Instanciation des quatre boutons radio. Chacun dentre eux se voit attribuer une
tiquette et une valeur diffrentes, mais tous sont associs la mme variable tkinter commune
(self.choixPolice). Tous invoquent galement la mme mthode self.changePolice(), chaque fois
que lutilisateur effectue un clic de souris sur lun ou lautre.
Lignes 28 31 : Le changement de police sobtient par reconfiguration de loption font du widget
Entry. Cette option attend un tuple contenant le nom de la police, sa taille, et ventuellement son
style. Si le nom de la police ne contient pas despaces, le tuple peut aussi tre remplac par une
chane de caractres. Exemples :
('Arial', 12, 'italic')
('Helvetica', 10)
('Times New Roman', 12, 'bold italic')
"Verdana 14 bold"
"President 18 italic"
(Voyez galement les exemples de la page 247).

Utilisation de cadres pour la composition dune fentre


Vous avez dj abondamment utilis la classe de widgets
Frame() ( cadre , en franais), notamment pour crer
de nouveaux widgets complexes par drivation.
Le petit script ci-dessous vous montre lutilit de cette
mme classe pour regrouper des ensembles de widgets
et les disposer dune manire dtermine dans une
fentre. Il vous dmontre galement lutilisation de
certaines options dcoratives (bordures, relief, etc.).
Pour composer la fentre ci-contre, nous avons utilis
deux cadres f1 et f2, de manire raliser deux groupes
de widgets bien distincts, lun gauche et lautre
droite. Nous avons color ces deux cadres pour bien les
mettre en vidence, mais ce nest videmment pas
indispensable.
Le cadre f1 contient lui-mme 6 autres cadres, qui
contiennent chacun un widget de la classe Label(). Le
cadre f2 contient un widget Canvas() et un widget Button(). Les couleurs et garnitures sont de simples
options.

220
1#
2#
3#
4#
5#
6#
7#
8#
9#
10#
11#
12#
13#
14#
15#
16#
17#
18#
19#
20#
21#
22#
23#
24#
25#
26#
27#
28#
29#
30#

Et pour quelques widgets de plus...


from tkinter import *
fen = Tk()
fen.title("Fentre compose l'aide de frames")
fen.geometry("300x300")
f1 = Frame(fen, bg = '#80c0c0')
f1.pack(side =LEFT, padx =5)
fint = [0]*6
for (n, col, rel, txt) in [(0, 'grey50', RAISED, 'Relief sortant'),
(1, 'grey60', SUNKEN, 'Relief rentrant'),
(2, 'grey70', FLAT, 'Pas de relief'),
(3, 'grey80', RIDGE, 'Crte'),
(4, 'grey90', GROOVE, 'Sillon'),
(5, 'grey100', SOLID, 'Bordure')]:
fint[n] = Frame(f1, bd =2, relief =rel)
e = Label(fint[n], text =txt, width =15, bg =col)
e.pack(side =LEFT, padx =5, pady =5)
fint[n].pack(side =TOP, padx =10, pady =5)
f2 = Frame(fen, bg ='#d0d0b0', bd =2, relief =GROOVE)
f2.pack(side =RIGHT, padx =5)
can = Canvas(f2, width =80, height =80, bg ='white', bd =2, relief =SOLID)
can.pack(padx =15, pady =15)
bou =Button(f2, text='Bouton')
bou.pack()
fen.mainloop()

Commentaires
Lignes 3 5 : Afin de simplifier au maximum la dmonstration, nous ne programmons pas cet
exemple comme une nouvelle classe. Remarquez la ligne 5 lutilit de la mthode geometry()
pour fixer les dimensions de la fentre principale.
Ligne 7 : Instanciation du cadre de gauche. La couleur de fond (une varit de bleu cyan) est
dtermine par largument bg (background). Cette chane de caractres contient en notation
hexadcimale la description des trois composantes rouge, verte et bleue de la teinte que lon
souhaite obtenir : aprs le caractre # signalant que ce qui suit est une valeur numrique
hexadcimale, on trouve trois groupes de deux symboles alphanumriques. Chacun de ces groupes
reprsente un nombre compris entre 1 et 255. Ainsi, 80 correspond 128, et c0 correspond 192
en notation dcimale. Dans notre exemple, les composantes rouge, verte et bleue de la teinte
reprsenter valent donc respectivement 128, 192 et 192.
En application de cette technique descriptive, le noir serait obtenu avec #000000, le blanc avec
#ffffff, le rouge pur avec #ff0000, un bleu sombre avec #000050, etc.
Ligne 8 : Puisque nous lui appliquons la mthode pack(), le cadre sera automatiquement
dimensionn par son contenu. Loption side =LEFT le positionnera gauche dans sa fentre
matresse. Loption padx =5 mnagera un espace de 5 pixels sa gauche et sa droite (nous
pouvons traduire padx par espacement horizontal ).
Ligne 10 : Dans le cadre f1 que nous venons de prparer, nous avons lintention de regrouper 6
autres cadres similaires contenant chacun une tiquette. Le code correspondant sera plus simple

Utilisation de cadres pour la composition dune fentre

221

et plus efficace si nous instancions ces widgets dans une liste plutt que dans des variables
indpendantes. Nous prparons donc cette liste avec 6 lments que nous remplacerons plus loin.
Lignes 11 16 : Pour construire nos 6 cadres similaires, nous allons parcourir une liste de 6 tuples
contenant les caractristiques particulires de chaque cadre. Chacun de ces tuples est constitu de
4 lments : un indice, une constante tkinter dfinissant un type de relief, et deux chanes de
caractres dcrivant respectivement la couleur et le texte de ltiquette.
La boucle for effectue 6 itrations pour parcourir les 6 lments de la liste. chaque itration, le
contenu dun des tuples est affect aux variables n, col, rel et txt (et ensuite les instructions des
lignes 17 20 sont excutes).
Le parcours dune liste de tuples laide dune boucle for constitue une construction
particulirement compacte, qui permet de raliser de nombreuses affectations avec un
trs petit nombre dinstructions.

Ligne 17 : Les 6 cadres sont instancis comme des lments de la liste fint. Chacun dentre eux est
agrment dune bordure dcorative de 2 pixels de large, avec un certain effet de relief.
Lignes 18-20 : Les tiquettes ont toutes la mme taille, mais leurs textes et leurs couleurs de fond
diffrent. Du fait de lutilisation de la mthode pack(), cest la dimension des tiquettes qui
dtermine la taille des petits cadres. Ceux-ci leur tour dterminent la taille du cadre qui les
regroupe (le cadre f1). Les options padx et pady permettent de rserver un petit espace autour de
chaque tiquette, et un autre autour de chaque petit cadre. Loption side =TOP positionne les 6
petits cadres les uns en dessous des autres dans le cadre conteneur f1.
Lignes 22-23 : Prparation du cadre f2 (cadre de droite). Sa couleur sera une varit de jaune, et
nous lentourerons dune bordure dcorative ayant laspect dun sillon.
Lignes 25 28 : Le cadre f2 contiendra un canevas et un bouton. Notez encore une fois lutilisation
des options padx et pady pour mnager des espaces autour des widgets (considrez par exemple
le cas du bouton, pour lequel cette option na pas t utilise : de ce fait, il entre en contact avec la
bordure du cadre qui lentoure). Comme nous lavons fait pour les cadres, nous avons plac une
bordure autour du canevas. Sachez que dautres widgets acceptent galement ce genre de
dcoration : boutons, champs dentre, etc.

Comment dplacer des dessins laide de la souris


Le widget canevas est lun des points forts de la bibliothque graphique tkinter. Il intgre en effet un
grand nombre de dispositifs trs efficaces pour manipuler des dessins. Le script ci-aprs est destin
vous montrer quelques techniques de base. Si vous voulez en savoir plus, notamment en ce qui
concerne la manipulation de dessins composs de plusieurs parties, veuillez consulter lun ou lautre
ouvrage de rfrence traitant de tkinter.
Au dmarrage de notre petite application, une srie de dessins sont tracs au hasard dans un canevas
(il sagit en loccurrence de simples ellipses colores). Vous pouvez dplacer nimporte lequel de ces
dessins en le saisissant laide de votre souris.
Lorsquun dessin est dplac, il passe lavant-plan par rapport aux autres, et sa bordure apparat plus
paisse pendant toute la dure de sa manipulation.

222

Et pour quelques widgets de plus...

Pour bien comprendre la technique utilise, vous devez vous rappeler quun logiciel utilisant une
interface graphique est un logiciel pilot par les vnements (revoyez au besoin les explications de
la page 83). Dans cette application, nous allons mettre en place un mcanisme qui ragit aux
vnements : enfoncement du bouton gauche de la souris , dplacement de la souris, le bouton
gauche restant enfonc , relchement du bouton gauche .
Ces vnements sont gnrs par le systme dexploitation et pris en charge par linterface tkinter.
Notre travail de programmation consistera donc les associer des gestionnaires diffrents
(fonctions ou mthodes).
Pour dvelopper cette petite application en suivant la philosophie objet , nous prfrerons crer
une nouvelle classe Bac_a_sable, drive du canevas de base, et y insrer la fonctionnalit souhaite,
plutt que de programmer cette fonctionnalit au niveau du corps principal du programme, en
agissant sur un canevas ordinaire. Ainsi, nous produisons du code rutilisable.
from tkinter import *
from random import randrange
class Bac_a_sable(Canvas):
"Canevas modifi pour prendre en compte quelques actions de la souris"
def __init__(self, boss, width=80, height=80, bg="white"):
# invocation du constructeur de la classe parente :
Canvas.__init__(self, boss, width=width, height=height, bg=bg)
# association-liaison d'vnements <souris> au prsent widget :
self.bind("<Button-1>", self.mouseDown)
self.bind("<Button1-Motion>", self.mouseMove)
self.bind("<Button1-ButtonRelease>", self.mouseUp)
def mouseDown(self, event):
"Opration effectuer quand le bouton gauche de la souris est enfonc"
self.currObject =None

Comment dplacer des dessins laide de la souris

223

# event.x et event.y contiennent les coordonnes du clic effectu :


self.x1, self.y1 = event.x, event.y
# <find_closest> renvoie la rfrence du dessin le plus proche :
self.selObject = self.find_closest(self.x1, self.y1)
# modification de l'paisseur du contour du dessin :
self.itemconfig(self.selObject, width =3)
# <lift> fait passer le dessin l'avant-plan :
self.lift(self.selObject)
def mouseMove(self, event):
"Op. effectuer quand la souris se dplace, bouton gauche enfonc"
x2, y2 = event.x, event.y
dx, dy = x2 -self.x1, y2 -self.y1
if self.selObject:
self.move(self.selObject, dx, dy)
self.x1, self.y1 = x2, y2
def mouseUp(self, event):
"Op. effectuer quand le bouton gauche de la souris est relch"
if self.selObject:
self.itemconfig(self.selObject, width =1)
self.selObject =None
if __name__ == '__main__':
# ---- Programme de test ---couleurs =('red','orange','yellow','green','cyan','blue','violet','purple')
fen =Tk()
# mise en place du canevas - dessin de 15 ellipses colores :
bac =Bac_a_sable(fen, width =400, height =300, bg ='ivory')
bac.pack(padx =5, pady =3)
# bouton de sortie :
b_fin = Button(fen, text ='Terminer', bg ='royal blue', fg ='white',
font =('Helvetica', 10, 'bold'), command =fen.quit)
b_fin.pack(pady =2)
# trac de 15 ellipses avec couleur et coordonnes alatoires :
for i in range(15):
coul =couleurs[randrange(8)]
x1, y1 = randrange(300), randrange(200)
x2, y2 = x1 + randrange(10, 150), y1 + randrange(10, 150)
bac.create_oval(x1, y1, x2, y2, fill =coul)
fen.mainloop()

Commentaires
Le script contient essentiellement la dfinition dune classe graphique drive de Canvas().
Cette nouvelle classe tant susceptible dtre rutilise dans dautres projets, nous plaons lensemble
du programme de test de cette classe dans la structure dsormais classique :
if __name__ ="__main__":. Ainsi, le script peut tre utilis tel quel, en tant que module importer,
pour dautres applications.
Le constructeur de notre nouveau widget Bac_a_sable() attend la rfrence du widget matre (boss)
comme premier paramtre, suivant la convention habituelle. Il fait appel au constructeur de la classe
parente, puis met en place des mcanismes locaux.

224

Et pour quelques widgets de plus...

En loccurrence, il sagit dassocier les trois identificateurs dvnements <Button-1>, <Button1Motion> et <Button1-ButtonRelease> aux noms des trois mthodes choisies comme gestionnaires de
ces vnements84.
Lorsque lutilisateur enfonce le bouton gauche de sa souris, la mthode mouseDown() est donc
active, et le systme dexploitation lui transmet en argument un objet event, dont les attributs x et y
contiennent les coordonnes du curseur souris dans le canevas, dtermines au moment du clic.
Nous mmorisons directement ces coordonnes dans les variables dinstance self.x1 et self.x2, car
nous en aurons besoin par ailleurs. Ensuite, nous utilisons la mthode find_closest() du widget
canevas, qui nous renvoie la rfrence du dessin le plus proche. Cette mthode bien pratique renvoie
toujours une rfrence, mme si le clic de souris na pas t effectu lintrieur du dessin.
Le reste est facile comprendre : la rfrence du dessin slectionn est mmorise dans une variable
dinstance, et nous pouvons faire appel dautres mthodes du canevas de base pour modifier ses
caractristiques. En loccurrence, nous utilisons les mthodes itemconfig() et lift() pour paissir son
contour et le faire passer au premier-plan.
Le transport du dessin est assur par la mthode mouseMove(), invoque chaque fois que la
souris se dplace alors que son bouton gauche est rest enfonc. Lobjet event contient cette fois
encore les coordonnes du curseur de la souris, au terme de ce dplacement. Nous nous en servons
pour calculer les diffrences entre ces nouvelles coordonnes et les prcdentes, afin de pouvoir les
transmettre la mthode move() du widget canevas, qui effectuera le transport proprement dit.
Nous ne pouvons cependant faire appel cette mthode que sil existe effectivement un objet
slectionn (cest le rle de la variable dinstance selObject), et il nous faut galement veiller
mmoriser les nouvelles coordonnes acquises.
La mthode mouseUp() termine le travail. Lorsque le dessin transport est arriv destination, il
reste annuler la slection et rendre au contour son paisseur initiale. Ceci ne peut tre envisag que
sil existe effectivement une slection, bien entendu.
Dans le corps du programme de test, nous instancions 15 dessins sans nous proccuper de conserver
leurs rfrences dans des variables. Nous pouvons procder ainsi parce que tkinter conserve lui-mme
une rfrence interne pour chacun de ces objets (cf. page 98)
Notez que si vous travaillez avec dautres bibliothques graphiques, vous devrez
probablement prvoir une mmorisation de ces rfrences.

Les dessins sont de simples ellipses colores. Leur couleur est choisie au hasard dans une liste de 8
possibilits, lindice de la couleur choisie tant dtermin par la fonction randrange() importe du
module random.

84 Rappel

: le gestionnaire dvnements ne transmettra les messages correspondants, que si les vnements


indiqus sont produits dans le canevas. Des clics de souris effectus en dehors ne produiront aucun effet.

Widgets complmentaires, widgets composites

225

Widgets complmentaires, widgets composites


Si vous explorez la volumineuse documentation que lon trouve sur lInternet concernant tkinter, vous
vous rendrez compte quil en existe diffrentes extensions, sous la forme de bibliothques annexes.
Ces extensions vous proposent des classes de widgets supplmentaires qui peuvent se rvler trs
prcieuses pour le dveloppement rapide dapplications complexes. Nous ne pouvons videmment pas
nous permettre de prsenter tous ces widgets dans le cadre restreint de ce cours dinitiation. Si cela
vous intresse, veuillez consulter les sites web traitant des bibliothques Tix et Ttk (entre autres). La
bibliothque Tix propose plus de 40 widgets complmentaires. La bibliothque Ttk est plutt destine
habiller les widgets avec diffrents thmes (styles de boutons, de fentres, etc.). Certaines de ces
bibliothques sont crites entirement en Python, comme Pmw (Python Mega Widgets).
Vous pouvez cependant faire une multitude de choses sans chercher dautres ressources que la
bibliothque standard tkinter. Vous pouvez en effet assez aisment construire vous-mme de nouvelles
classes de widgets composites adaptes vos besoins. Cela peut vous demander un certain travail au
dpart, mais en procdant ainsi, vous contrlez trs prcisment ce que contiennent vos applications,
et vous garantissez leur portabilit sur tous les systmes qui acceptent Python, puisque tkinter fait
partie de la distribution standard du langage. En effet, lorsque vous utilisez des bibliothques tierces,
vous devez toujours vrifier leur disponibilit et leur compatibilit pour les machines cibles de vos
programmes, et prvoir leur installation, si ncessaire.
Les pages qui suivent expliquent les principes gnraux mettre en uvre pour raliser vous-mme
des classes de widgets composites, avec quelques exemples parmi les plus utiles.

Combo box simplifi


La petite application ci-aprs vous montre comment construire une nouvelle classe de widget de type
Combo box. On appelle ainsi un widget qui associe un champ dentre une bote de liste :
lutilisateur peut entrer dans le systme soit un des lments de la liste propose (en cliquant sur son
nom), soit un lment non rpertori (en saisissant un nouveau nom dans le champ dentre). Nous
avons un peu simplifi le problme en laissant la liste visible en permanence, mais il est parfaitement
possible de perfectionner ce widget pour quil prenne la forme classique dun champ dentre assorti
dun petit bouton provoquant lapparition de la liste, celle-ci tant cache au dpart (Voir exercice
14.1, page 253).
Tel que nous limaginons, notre widget combo va donc regrouper en une seule entit trois widgets de
base tkinter : un champ dentre, une bote de liste (listbox) et un ascenseur (barre de dfilement
vertical ou scrollbar).
La bote de liste et son ascenseur seront troitement lis, puisque lascenseur permet de faire dfiler la
liste dans sa bote. Il faudra dailleurs sassurer que lascenseur ait toujours la mme hauteur que la
bote, quelle que soit la taille choisie pour celle-ci.
Nous allons donc placer la bote de liste et son ascenseur cte cte dans un cadre (Frame), et placer
celui-ci avec son contenu en-dessous du champ dentre, dans un autre cadre plus global. Lensemble
constituera notre widget composite.

226

Et pour quelques widgets de plus...

Pour tester notre widget, nous lincluons dans une petite


application trs simple : lorsque lutilisateur choisit une
couleur dans la liste (il peut aussi entrer un nom de couleur
directement dans le champ dentre), cette couleur devient
automatiquement la couleur de fond pour la fentre matresse.
Dans cette fentre matresse, nous avons ajout un libell et un
bouton, afin de vous montrer comment vous pouvez accder
la slection opre prcdemment dans le ComboBox
lui-mme (le bouton provoque laffichage du nom de la
dernire couleur choisie).
1# from tkinter import *
2#
3# class ComboBox(Frame):
4#
"Widget composite associant un champ d'entre avec une bote de liste"
5#
def __init__(self, boss, item='', items=[], command ='', width =10,
6#
listSize =5):
7#
Frame.__init__(self, boss)
# constructeur de la classe parente
8#
# (<boss> est la rf. du widget 'matre')
9#
self.items =items
# items placer dans la bote de liste
10#
self.command =command
# fonction invoquer aprs clic ou <Enter>
11#
self.item =item
# item entr ou slectionn
12#
13#
# Champ d'entre :
14#
self.entree =Entry(self, width =width)
# largeur en caractres
15#
self.entree.insert(END, item)
16#
self.entree.bind("<Return>", self.sortieE)
17#
self.entree.pack(side =TOP)
18#
19#
# Bote de liste, munie d'un ascenseur (scroll bar) :
20#
cadreLB =Frame(self)
# cadre pour l'ensemble des 2
21#
self.bListe =Listbox(cadreLB, height =listSize, width =width-1)
22#
scrol =Scrollbar(cadreLB, command =self.bListe.yview)
23#
self.bListe.config(yscrollcommand =scrol.set)
24#
self.bListe.bind("<ButtonRelease-1>", self.sortieL)
25#
self.bListe.pack(side =LEFT)
26#
scrol.pack(expand =YES, fill =Y)
27#
cadreLB.pack()
28#
29#
# Remplissage de la bote de liste avec les items fournis :
30#
for it in items:
31#
self.bListe.insert(END, it)
32#
33#
def sortieL(self, event =None):
34#
# Extraire de la liste l'item qui a t slectionn :
35#
index =self.bListe.curselection()
# renvoie un tuple d'index
36#
ind0 =int(index[0])
# on ne garde que le premier
37#
self.item =self.items[ind0]
38#
# Actualiser le champ d'entre avec l'item choisi :
39#
self.entree.delete(0, END)
40#
self.entree.insert(END, self.item)
41#
# Excuter la commande indique, avec l'item choisi comme argument :
42#
self.command(self.item)
43#
44#
def sortieE(self, event =None):
45#
# Excuter la commande indique, avec l'argument-item encod tel quel :
46#
self.command(self.entree.get())

Widgets complmentaires, widgets composites

227

47#
48#
def get(self):
49#
# Renvoyer le dernier item slectionn dans la bote de liste
50#
return self.item
51#
52# if __name__ =="__main__":
# --- Programme de test --53#
def changeCoul(col):
54#
fen.configure(background = col)
55#
56#
def changeLabel():
57#
lab.configure(text = combo.get())
58#
59#
couleurs = ('navy', 'royal blue', 'steelblue1', 'cadet blue',
60#
'lawn green', 'forest green', 'yellow', 'dark red',
61#
'grey80','grey60', 'grey40', 'grey20', 'pink')
62#
fen =Tk()
63#
combo =ComboBox(fen, item ="nant", items =couleurs, command =changeCoul,
64#
width =15, listSize =6)
65#
combo.grid(row =1, columnspan =2, padx =10, pady =10)
66#
bou = Button(fen, text ="Test", command =changeLabel)
67#
bou.grid(row =2, column =0, padx =8, pady =8)
68#
lab = Label(fen, text ="Bonjour", bg ="ivory", width =15)
69#
lab.grid(row =2, column =1, padx =8)
70#
fen.mainloop()

Commentaires

Lignes 5-8 : le constructeur de notre widget attend la rfrence du widget matre ( boss) comme
premier paramtre, suivant la convention habituelle. Les autres paramtres permettent
notamment de prvoir un texte par dfaut dans le champ dentre ( item), de fournir la liste des
lments insrer dans la bote (items), et de dsigner la fonction invoquer lorsque lutilisateur
cliquera dans la liste, ou enfoncera la touche <Enter> de son clavier ( command). Nous avons
conserv des noms anglais pour ces paramtres, afin que notre widget puisse tre utilis avec les
mmes conventions que les widgets de base dont il drive.
Lignes 15, 39, 40 : les mthodes du widget Entry ont dj t dcrites prcdemment (cf. page 218).
Rappelons simplement que la mthode insert() permet dinsrer du texte dans le champ, sans
faire disparatre un ventuel texte prexistant. Le premier argument permet de prciser quel
endroit du texte prexistant linsertion doit avoir lieu. Ce peut tre un entier, ou bien une valeur
symbolique (en important lensemble du module tkinter la ligne 1, on a import une srie de
variables globales, dont END, qui contiennent ces valeurs symboliques, et END dsigne bien entendu
la fin du texte prexistant).
Lignes 16 et 24 : deux vnements seront associs des mthodes locales : le fait de relcher le
bouton droit de la souris alors que son pointeur se trouve dans la bote de liste (vnement
<ButtonRelease-1>) et le fait denfoncer la touche <Enter> (vnement <Return>).
Ligne 21 : cration de la bote de liste (classe de base Listbox). Sa largeur sexprime en nombre de
caractres de la police courante. On en retranche un ou deux, afin de compenser
approximativement la place quoccupera lascenseur (lensemble des deux devant avoir peu prs
la mme largeur que le champ dentre).
Ligne 22 : cration de la barre de dfilement verticale (classe de base Scrollbar). La commande
quon lui associe command =self.bListe.yview indique la mthode du widget Listbox qui sera in-

228

Et pour quelques widgets de plus...


voque pour provoquer le dfilement de la liste dans la bote, lorsque lon actionnera cet
ascenseur.
Ligne 23 : symtriquement, on doit reconfigurer la bote de liste pour lui indiquer quelle mthode
du widget Scrollbar invoquer, afin que la position de lascenseur reflte correctement la position
relative de litem slectionn dans la liste. Il ntait pas possible dindiquer cette commande dans
la ligne dinstruction crant la bote de liste, la ligne 21, car ce moment-l le widget Scrollbar
nexistait pas encore. Y faire rfrence tait donc exclu85.
Ligne 33 : cette mthode est invoque chaque fois que lutilisateur slectionne un lment dans la
liste. Elle fait appel la mthode curselection() du widget Listbox de base. Celle-ci lui renvoie un
tuple dindices, car il a t prvu par les dveloppeurs de tkinter que lutilisateur puisse
slectionner plusieurs items dans la liste ( laide de la touche <Ctrl>). Nous supposerons
cependant ici quun seul a t point, et rcuprons donc seulement le premier lment de ce
tuple. la ligne 47, nous pouvons alors extraire litem correspondant de la liste et lutiliser, la
fois pour mettre jour le champ dentre (lignes 39-40), ainsi que comme argument pour excuter
la commande (ligne 42) dont la rfrence avait t fournie lors de linstanciation du widget (dans
le cas de notre petite application, ce sera donc la fonction changeCoul()).
Lignes 44-46 : la mme commande est invoque lorsque lutilisateur actionne la touche <Enter>
aprs avoir encod une chane de caractres dans le champ dentre. Le paramtre event, non
utilis ici, permettrait de rcuprer le ou les vnements associs.
Lignes 48-49 : nous avons aussi inclus une mthode get(), suivant la convention suivie par dautres
widgets, afin de permettre la rcupration libre du dernier item slectionn.

Le widget Text assorti dun ascenseur


En procdant de la mme manire que dans lexemple prcdent, vous pouvez associer les widgets
standard tkinter de multiples faons. Ainsi nous vous prsentons ci-aprs un widget composite qui
pourrait vous servir baucher un systme de traitement de texte rudimentaire. Son principal
composant est le widget Text standard, qui peut afficher des textes formats, cest--dire des textes
intgrant divers attributs de style (comme le gras, litalique, lexposant...), ainsi que des polices de
caractres diffrentes, de la couleur, et mme des images. Nous lavons simplement associ une barre
de dfilement verticale pour vous montrer, une fois de plus, les interactions que vous pouvez crer
entre ces composants.
Le widget Text est capable dinterprter tout un systme de balises insres nimporte o dans le
texte. Avec elles, vous pouvez fixer des repres, tablir des liens, et rendre cliquables les lments
affichs (textes ou images), de manire vous en servir pour dclencher toutes sortes de mcanismes.

85 Nous

aurions pu aussi faire exactement linverse, cest--dire crer dabord lascenseur sans indication de
commande, puis crer la bote de liste en indiquant la commande daccs lascenseur dans la ligne
dinstanciation, et enfin reconfigurer lascenseur en lui indiquant la commande de dfilement de la liste :
scrol =Scrollbar(cadreLB)
self.bListe =Listbox(cadreLB, height =listSize, width =width-1,
yscrollcommand =scrol.set)
scrol.config(command =self.bListe.yview)

Widgets complmentaires, widgets composites

229

Par exemple, dans lapplication dcrite ci-aprs,


le fait de cliquer sur le nom Jean de la
Fontaine , laide du bouton droit de la souris,
provoque le dfilement automatique du texte
(scrolling), jusqu ce quune rubrique dcrivant
cet auteur devienne visible dans le widget (voir
le script correspondant page suivante). Dautres
fonctionnalits sont prsentes, telles la
possibilit de slectionner laide de la souris
nimporte quelle portion du texte affich pour
lui faire subir un traitement quelconque, mais
nous ne prsenterons ici que les plus
fondamentales. Veuillez donc consulter les
ouvrages ou sites web spcialiss pour en savoir davantage.
Gestion du texte affich
Vous pouvez accder nimporte quelle portion du texte pris en charge par un widget Text grce
deux concepts complmentaires, les index et les balises :

Chaque caractre du texte affich est rfrenc par un index, lequel doit tre une chane de
caractres contenant deux valeurs numriques relies par un point (ex : "5.2"). Ces deux valeurs
indiquent respectivement le numro de ligne et le numro de colonne o se situe le caractre.
Nimporte quelle portion du texte peut tre associe une ou plusieurs balises, dont vous
choisissez librement le nom et les proprits. Celles-ci vous permettent de dfinir la police, les
couleurs davant et darrire-plans, les vnements associs, etc.
Pour la bonne comprhension du script ci-dessous, veuillez considrer que le texte de la
fable traite doit tre accessible, dans un fichier nomm CorbRenard.txt , encod en
latin-1.
1# from tkinter import *
2#
3# class ScrolledText(Frame):
4#
"""Widget composite, associant un widget Text et une barre de dfilement"""
5#
def __init__(self, boss, baseFont ="Times", width =50, height =25):
6#
Frame.__init__(self, boss, bd =2, relief =SUNKEN)
7#
self.text =Text(self, font =baseFont, bg ='ivory', bd =1,
8#
width =width, height =height)
9#
scroll =Scrollbar(self, bd =1, command =self.text.yview)
10#
self.text.configure(yscrollcommand =scroll.set)
11#
self.text.pack(side =LEFT, expand =YES, fill =BOTH, padx =2, pady =2)
12#
scroll.pack(side =RIGHT, expand =NO, fill =Y, padx =2, pady =2)
13#
14#
def importFichier(self, fichier, encodage ="Utf8"):
15#
"insertion d'un texte dans le widget, partir d'un fichier"
16#
of =open(fichier, "r", encoding =encodage)
17#
lignes =of.readlines()
18#
of.close()
19#
for li in lignes:
20#
self.text.insert(END, li)

230
21#
22#
23#
24#
25#
26#
27#
28#
29#
30#
31#
32#
33#
34#
35#
36#
37#
38#
39#
40#
41#
42#
43#
44#
45#
46#
47#
48#
49#
50#
51#
52#
53#
54#
55#
56#
57#
58#
59#
60#
61#
62#
63#
64#

Et pour quelques widgets de plus...


def chercheCible(event=None):
"dfilement du texte jusqu' la balise <cible>, grce la mthode see()"
index = st.text.tag_nextrange('cible', '0.0', END)
st.text.see(index[0])
### Programme principal : fentre avec un libell et un 'ScrolledText' ###
fen =Tk()
lib =Label(fen, text ="Widget composite : Text + Scrollbar",
font ="Times 14 bold italic", fg ="navy")
lib.pack(padx =10, pady =4)
st =ScrolledText(fen, baseFont="Helvetica 12 normal", width =40, height =10)
st.pack(expand =YES, fill =BOTH, padx =8, pady =8)
# Dfinition de balises, liaison d'un vnement <clic du bouton droit> :
st.text.tag_configure("titre", foreground ="brown",
font ="Helvetica 11 bold italic")
st.text.tag_configure("lien", foreground ="blue",
font ="Helvetica 11 bold")
st.text.tag_configure("cible", foreground ="forest green",
font ="Times 11 bold")
st.text.tag_bind("lien", "<Button-3>", chercheCible)
titre ="""Le Corbeau et le Renard
par Jean de la Fontaine, auteur franais
\n"""
auteur ="""
Jean de la Fontaine
crivain franais (1621-1695)
clbre pour ses Contes en vers,
et surtout ses Fables, publies
de 1668 1694."""
# Remplissage du widget Text (2 techniques) :
st.importFichier("CorbRenard.txt", encodage ="Latin1")
st.text.insert("0.0", titre, "titre")
st.text.insert(END, auteur, "cible")
# Insertion d'une image :
photo =PhotoImage(file= "penguin.gif")
st.text.image_create("6.14", image =photo)
# Ajout d'une balise supplmentaire :
st.text.tag_add("lien", "2.4", "2.23")
fen.mainloop()

Commentaires

Lignes 3 6 : le widget composite que nous dfinissons dans cette classe sera une fois de plus
obtenu par drivation de la classe Frame(). Son constructeur prvoit quelques paramtres
dinstanciation titre dexemple (police utilise, largeur et hauteur), avec des valeurs par dfaut.
Ces paramtres seront simplement transmis au widget Text interne (aux lignes 7 et 8). Vous
pourriez bien videmment en ajouter beaucoup dautres, pour dterminer par exemple
lapparence du curseur, la couleur du fond ou des caractres, la manire dont les lignes trop
longues doivent tre coupes ou non, etc. Vous pourriez aussi de la mme faon transmettre
divers paramtres la barre de dfilement.
Lignes 7 10 : comme nous lavons dj expliqu prcdemment (pour le widget ComboBox), il faut
trois lignes dinstructions pour tablir les interactions rciproques des deux widgets Scrollbar et

Widgets complmentaires, widgets composites

231

Text. Aprs linstanciation du widget Text aux lignes 7 et 8, on cre la barre de dfilement la ligne
9, en prcisant dans linstruction dinstanciation la mthode du widget Text qui sera sous contrle
de lascenseur. On reconfigure ensuite le widget Text la ligne 10, pour lui indiquer en retour
quelle mthode de lascenseur il devra invoquer afin de maintenir celui-ci la bonne hauteur, en
fonction du dfilement effectif du texte. Il nest pas possible dindiquer cette rfrence lors de la
cration du widget Text aux lignes 7 et 8, parce qu ce moment lascenseur nexiste pas encore.
Lignes 11 et 12 : loption expand de la mthode pack() naccepte que les valeurs YES ou NO. Elle
dtermine si le widget doit stirer lorsque la fentre est redimensionne. Loption
complmentaire fill peut prendre les 3 valeurs suivantes : X, Y ou BOTH. Elle indique si
ltirement seffectue horizontalement (axe X), verticalement (axe Y), ou dans les deux directions
(BOTH). Lorsque vous dveloppez une application, il est important de prvoir ces
redimensionnements ventuels, surtout si lapplication est destine tre utilise dans des
environnements varis (Windows, Linux, Mac OS).
Lignes 22 25 : cette fonction est un gestionnaire dvnements, qui est appel lorsque
lutilisateur clique avec le bouton droit sur le nom de lauteur (lvnement en question est associ
la balise correspondante, la ligne 42).
la ligne 24, on utilise la mthode tag_nextrange() du widget Text pour trouver les index de la
portion de texte associe la balise cible . La recherche de ces index est limite au domaine
dfini par les 2e et 3e arguments (dans notre exemple, on recherche du dbut la fin du texte
entier). La mthode tag_nextrange() renvoie un tuple de deux index (ceux des premiers et
derniers caractres de la portion de texte associe la balise cible ). la ligne 25, nous nous
servons dun seul de ces index (le premier) pour activer la mthode see(). Celle-ci provoque un
dfilement automatique du texte (scrolling), de telle manire que le caractre correspondant
lindex transmis devienne visible dans le widget (avec en gnral un certain nombre des
caractres qui suivent).
Lignes 27 33 : construction classique dune fentre contenant deux widgets.
Lignes 35 42 : ces lignes dfinissent les trois balises titre, lien et cible ainsi que le formatage du
texte qui leur sera associ. La ligne 42 prcise en outre que le texte associ la balise lien sera
cliquable (le bouton numro 3 de la souris est le bouton droit), avec indication du gestionnaire
dvnements correspondant.
Ligne 55 : vous pouvez incorporer nimporte quelle fonctionnalit dans la dfinition dune classe,
comme nous lavons fait ici en prvoyant une mthode dimportation de fichier texte dans le
widget lui-mme, avec le paramtre ad hoc pour un dcodage ventuel. Avec cette mthode, le
texte import sinsre la fin du texte dj prsent dans le widget, mais vous pourriez aisment
lamliorer en lui ajoutant un paramtre pour prciser lendroit exact o linsertion doit avoir
lieu.
Lignes 56-57 : ces instructions insrent des fragments de texte (respectivement au dbut et la fin
du texte prexistant), en associant une balise chacun deux.
Ligne 62 : lassociation des balises au texte est dynamique. tout moment, vous pouvez activer
une nouvelle association (comme nous le faisons ici en rattachant la balise lien une portion
de texte prexistante). Pour dtacher une balise, utilisez la mthode tag_delete().

232

Et pour quelques widgets de plus...

Canevas avec barres de dfilement


Nous avons dj beaucoup exploit le widget Canvas, dont les possibilits sont extrmement tendues.
Nous avons vu comment encore enrichir cette classe par drivation. Cest ce que nous allons faire une
fois de plus dans lexemple ci-aprs, avec la dfinition dune nouvelle classe ScrolledCanvas, dans
laquelle nous associerons au canevas standard deux barres de dfilement (une verticale et une
horizontale).
Afin de rendre lexercice plus attrayant, nous nous servirons de notre nouvelle classe de widget pour
raliser un petit jeu dadresse, dans lequel lutilisateur devra russir cliquer sur un bouton qui
sesquive sans cesse.

Le widget Canvas est trs polyvalent : il vous permet de combiner volont des dessins, des images
bitmap, des fragments de texte, et mme dautres widgets, dans un espace parfaitement extensible. Si
vous souhaitez dvelopper lun ou lautre jeu graphique, cest videmment le widget quil vous faut
apprendre matriser en priorit.
Cependant, comprenez bien que les indications que nous vous fournissons ce sujet dans les prsentes
notes sont forcment trs incompltes. Leur objectif est seulement de vous aider comprendre les
concepts de base, afin que vous puissiez ensuite consulter les ouvrages de rfrence spcialiss dans
de bonnes conditions.
Notre petite application se prsente comme une nouvelle classe FenPrinc(), obtenue par drivation
partir de la classe de base Tk(). Elle contient deux widgets : un simple libell et notre nouveau widget
composite ScrolledCanvas. Celui-ci est une vue sur un espace de dessin beaucoup plus grand, dans
lequel nous pourrons voyager grce aux barres de dfilement.
Afin que lespace disponible soit bien reprable, nous commenons par y planter un dcor simple,
constitu de 80 ellipses de couleur dont lemplacement et les dimensions sont tirs au hasard. Nous y

Widgets complmentaires, widgets composites

233

ajoutons galement un petit clin dil sous la forme dune image bitmap, destine avant tout vous
rappeler comment grer ce type de ressource.
Pour terminer, nous y installons aussi un vritable widget fonctionnel : en loccurrence un simple
bouton, mais la technique mise en uvre pourrait sappliquer nimporte quel autre type de widget, y
compris un gros widget composite comme ceux que nous avons dvelopps prcdemment. Cette
grande souplesse dans le dveloppement dapplications complexes est lun des principaux bnfices
apports par le mode de programmation oriente objet.
Le bouton sanime ds quon la enfonc une premire fois, et lanimation sarrte si on arrive
nouveau cliquer dessus. Dans votre analyse du script ci-aprs, soyez attentif aux mthodes utilises
pour modifier les proprits dun objet existant.
1#
2#
3#
4#
5#
6#
7#
8#
9#
10#
11#
12#
13#
14#
15#
16#
17#
18#
19#
20#
21#
22#
23#
24#
25#
26#
27#
28#
29#
30#
31#
32#
33#
34#
35#
36#
37#
38#
39#
40#
41#
42#
43#
44#
45#
46#
47#

from tkinter import *


from random import randrange
class ScrolledCanvas(Frame):
"""Canevas extensible avec barres de dfilement"""
def __init__(self, boss, width =100, height =100, bg="white", bd=2,
scrollregion =(0, 0, 300, 300), relief=SUNKEN):
Frame.__init__(self, boss, bd =bd, relief=relief)
self.can =Canvas(self, width=width-20, height=height-20, bg=bg,
scrollregion =scrollregion, bd =1)
self.can.grid(row =0, column =0)
bdv =Scrollbar(self, orient =VERTICAL, command =self.can.yview, bd =1)
bdh =Scrollbar(self, orient =HORIZONTAL, command =self.can.xview, bd =1)
self.can.configure(xscrollcommand =bdh.set, yscrollcommand =bdv.set)
bdv.grid(row =0, column =1, sticky = NS)
# sticky =>
bdh.grid(row =1, column =0, sticky = EW)
# tirer la barre
# Lier l'vnement <redimensionnement> un gestionnaire appropri :
self.bind("<Configure>", self.redim)
self.started =False
def redim(self, event):
"oprations effectuer chaque redimensionnement du widget"
if not self.started:
self.started =True
# Ne pas redimensionner ds la cration
return
# du widget (sinon => bouclage)
# partir des nouvelles dimensions du cadre, redimensionner le canevas
# (la diff. de 20 pixels sert compenser l'paisseur des scrollbars) :
larg, haut = self.winfo_width()-20, self.winfo_height()-20
self.can.config(width =larg, height =haut)
class FenPrinc(Tk):
def __init__(self):
Tk.__init__(self)
self.libelle =Label(text ="Scroll game", font="Helvetica 14 bold")
self.libelle.pack(pady =3)
terrainJeu =ScrolledCanvas(self, width =500, height =300, relief=SOLID,
scrollregion =(-600,-600,600,600), bd =3)
terrainJeu.pack(expand =YES, fill =BOTH, padx =6, pady =6)
self.can =terrainJeu.can
# Dcor : trac d'une srie d'ellipses alatoires :
coul =('sienna','maroon','brown','pink','tan','wheat','gold','orange',
'plum','red','khaki','indian red','thistle','firebrick',
'salmon','coral','yellow','cyan','blue','green')
for r in range(80):
x1, y1 = randrange(-600,450), randrange(-600,450)
x2, y2 = x1 +randrange(40,300), y1 +randrange(40,300)
couleur = coul[randrange(20)]

234

Et pour quelques widgets de plus...

48#
self.can.create_oval(x1, y1, x2, y2, fill=couleur, outline='black')
49#
# Ajout d'une petite image GIF :
50#
self.img = PhotoImage(file ='linux2.gif')
51#
self.can.create_image(50, 20, image =self.img)
52#
# Bouton attraper :
53#
self.x, self.y = 50, 100
54#
self.bou = Button(self.can, text ="Start", command =self.start)
55#
self.fb = self.can.create_window(self.x, self.y, window =self.bou)
56#
57#
def anim(self):
58#
if self.run ==0:
59#
return
60#
self.x += randrange(-60, 61)
61#
self.y += randrange(-60, 61)
62#
self.can.coords(self.fb, self.x, self.y)
63#
self.libelle.config(text = 'Cherchez en %s %s' % (self.x, self.y))
64#
self.after(250, self.anim)
65#
66#
def stop(self):
67#
self.run =0
68#
self.bou.configure(text ="Start", command =self.start)
69#
70#
def start(self):
71#
self.bou.configure(text ="Attrapez-moi !", command =self.stop)
72#
self.run =1
73#
self.anim()
74#
75# if __name__ =="__main__":
# --- Programme de test --76#
FenPrinc().mainloop()

Commentaires

Lignes 6 10 : comme beaucoup dautres, notre nouveau widget est driv de Frame(). Son
constructeur accepte un certain nombre de paramtres. Remarquez bien que ces paramtres sont
transmis pour partie au cadre (paramtres bd, relief), et pour partie au canevas (paramtres width,
height, bg, scrollregion). Vous pouvez bien videmment faire dautres choix selon vos besoins.
Loption scrollregion du widget Canvas sert dfinir lespace de dessin dans lequel la vue du
canevas pourra se dplacer.
Lignes 11 16 : nous utiliserons cette fois la mthode grid() pour mettre en place le canevas et ses
barres de dfilement (cette mthode vous a t prsente page 95). La mthode pack() ne
conviendrait gure pour mettre en place correctement les deux barres de dfilement, car elle
imposerait lutilisation de plusieurs cadres ( frames) imbriqus (essayez, titre dexercice !). Les
interactions mettre en place entre les barres de dfilement et le widget quelles contrlent
(lignes 12, 13, 14) ont dj t dcrites en dtail pour les deux widgets composites prcdents.
Loption orient des barres de dfilement navait pas t utilise jusquici, parce que sa valeur par
dfaut (VERTICAL) convenait aux cas traits.
Aux lignes 15 et 16, les options sticky =NS et sticky =EW provoquent ltirement des barres de
dfilement jusqu occuper toute la hauteur (NS = direction Nord-Sud) ou toute la largeur (EW =
direction Est-Ouest) de la cellule dans la grille. Il ny aura cependant pas de redimensionnement
automatique, comme cest le cas avec la mthode pack() (les options expand et fill ne sont
dailleurs pas disponibles).

Widgets complmentaires, widgets composites

235

Ligne 18 : puisque la mthode grid() ninclut pas le redimensionnement automatique, nous devons
guetter lvnement qui est gnr par le systme lorsque lutilisateur redimensionne le widget, et
lassocier une mthode approprie pour effectuer nous-mmes le redimensionnement des
composants du widget.
Lignes 19 29 : la mthode de redimensionnement consistera simplement redimensionner le
canevas (les barres de dfilement sadapteront toutes seules, du fait de loption sticky qui leur est
applique). Notez au passage que vous pouvez trouver les dimensions actualises dun widget dans
ses attributs winfo_width() et winfo_height().
La variable dinstance self.started est un simple interrupteur, qui permet dviter que le
redimensionnement soit appel prmaturment, lors de linstanciation du widget (ce qui produit
un bouclage curieux : essayez sans !).
Lignes 31 55 : cette classe dfinit notre petite application de jeu. Son constructeur instancie
notre nouveau widget dans la variable terrainJeu (ligne 36). Remarquez que le type de bordure et
son paisseur sappliqueront au cadre du widget composite, alors que les autres arguments choisis
sappliqueront au canevas. Avec loption scrollregion, nous dfinissons un espace de jeu nettement
plus tendu que la surface du canevas lui-mme, ce qui obligera le joueur dplacer (ou
redimensionner) celui-ci.
Lignes 54-55 : cest la mthode create_window() du widget Canvas qui permet dy insrer
nimporte quel autre widget (y compris un widget composite). Le widget insrer doit cependant
avoir t dfini lui-mme au pralable comme un esclave du canevas ou de sa fentre matresse. La
mthode create_window() attend trois arguments : les coordonnes X et Y du point o lon
souhaite insrer le widget, et la rfrence de ce widget.
Lignes 57 64 : cette mthode est utilise pour lanimation du bouton. Aprs avoir repositionn le
bouton au hasard une certaine distance de sa position prcdente, elle se r-appelle elle-mme
aprs une pause de 250 millisecondes. Ce bouclage seffectue sans cesse, aussi longtemps que la
variable self.run contient une valeur non nulle.
Lignes 66 73 : ces deux gestionnaires dvnements sont associs au bouton en alternance. Ils
servent videmment dmarrer et arrter lanimation.

Application fentres multiples paramtrage implicite


La classe Toplevel() de tkinter permet de crer des fentres satellites de votre application
principale. Ces fentres sont autonomes, mais elles se refermeront automatiquement lorsque vous
fermerez la fentre principale. Cette restriction mise part, elles se traitent de la manire habituelle :
vous pouvez y placer nimporte quelle combinaison de widgets, votre guise.
La petite application ci-aprs vous montre quelques-unes de leurs possibilits. Elle est constitue
dune fentre principale trs ordinaire, contenant simplement trois boutons. Ces boutons sont crs
partir dune classe drive de la classe Button() de base, ceci afin de vous montrer encore une fois
combien il est facile dadapter les classes dobjets existantes vos besoins. Vous pourrez noter au
passage quelques options dcoratives intressantes.
Le bouton <Top1> fait apparatre une premire fentre satellite contenant un canevas avec une image.
Nous avons dot cette fentre de proprits particulires : elle ne possde ni bandeau-titre, ni bor-

236

Et pour quelques widgets de plus...

dure, et il est impossible de la redimensionner laide de la souris. De plus, cette fentre est modale : on
qualifie ainsi une fentre qui reste toujours au premier plan, devant toutes les autres fentres
dapplication ventuellement prsentes lcran.

Le bouton <Top2> fait apparatre une deuxime fentre satellite plus classique, qui contient deux
exemplaires dun petit widget composite SpinBox que nous avons cr en suivant les principes dcrits
dans les pages prcdentes. Ce widget se compose de deux boutons et dun libell indiquant une valeur
numrique. Les boutons permettent daugmenter ou de diminuer la valeur affiche. En plus de ces
deux SpinBoxes, la fentre contient un gros bouton dcor. En lactionnant, lutilisateur provoque le
redimensionnement du canevas dans lautre fentre satellite, en accord avec les valeurs numriques
affiches dans les deux SpinBoxes.
1# from tkinter import *
2#
3# class FunnyButton(Button):
4#
"Bouton de fantaisie : vert virant au rouge quand on l'actionne"
5#
def __init__(self, boss, **Arguments):
6#
Button.__init__(self, boss, bg ="dark green", fg ="white", bd =5,
7#
activebackground ="red", activeforeground ="yellow",
8#
font =('Helvetica', 12, 'bold'), **Arguments)
9#
10# class SpinBox(Frame):
11#
"widget composite comportant un Label et 2 boutons 'up' & 'down'"
12#
def __init__(self, boss, largC=5, largB =2, vList=[0], liInd=0, orient =Y):
13#
Frame.__init__(self, boss)
14#
self.vList =vList
# liste des valeurs prsenter
15#
self.liInd =liInd
# index de la valeur montrer par dfaut
16#
if orient ==Y:
17#
s, augm, dimi = TOP, "^", "v"
# Orientation 'verticale'
18#
else:
19#
s, augm, dimi = RIGHT, ">", "<"
# Orientation 'horizontale'
20#
Button(self, text =augm, width =largB, command =self.up).pack(side =s)
21#
self.champ = Label(self, bg ='white', width =largC,
22#
text =str(vList[liInd]), relief =SUNKEN)
23#
self.champ.pack(pady =3, side =s)
24#
Button(self, text=dimi, width=largB, command =self.down).pack(side =s)

Application fentres multiples paramtrage implicite

237

25#
26#
def up(self):
27#
if self.liInd < len(self.vList) -1:
28#
self.liInd += 1
29#
else:
30#
self.bell()
# mission d'un bip
31#
self.champ.configure(text =str(self.vList[self.liInd]))
32#
33#
def down(self):
34#
if self.liInd > 0:
35#
self.liInd -= 1
36#
else:
37#
self.bell()
# mission d'un bip
38#
self.champ.configure(text =str(self.vList[self.liInd]))
39#
40#
def get(self):
41#
return self.vList[self.liInd]
42#
43# class FenDessin(Toplevel):
44#
"Fentre satellite (modale) contenant un simple canevas"
45#
def __init__(self, **Arguments):
46#
Toplevel.__init__(self, **Arguments)
47#
self.geometry("250x200+100+240")
48#
self.overrideredirect(1)
# => fentre sans bordure ni bandeau
49#
self.transient(self.master)
# => fentre 'modale'
50#
self.can =Canvas(self, bg="ivory", width =200, height =150)
51#
self.img = PhotoImage(file ="papillon2.gif")
52#
self.can.create_image(90, 80, image =self.img)
53#
self.can.pack(padx =20, pady =20)
54#
55# class FenControle(Toplevel):
56#
"Fentre satellite contenant des contrles de redimensionnement"
57#
def __init__(self, boss, **Arguments):
58#
Toplevel.__init__(self, boss, **Arguments)
59#
self.geometry("250x200+400+230")
60#
self.resizable(width =0, height =0)
# => empche le redimensionnement
61#
p =(10, 30, 60, 90, 120, 150, 180, 210, 240, 270, 300)
62#
self.spX =SpinBox(self, largC=5,largB =1,vList =p,liInd=5,orient =X)
63#
self.spX.pack(pady =5)
64#
self.spY =SpinBox(self, largC=5,largB =1,vList =p,liInd=5,orient =Y)
65#
self.spY.pack(pady =5)
66#
FunnyButton(self, text ="Dimensionner le canevas",
67#
command =boss.redimF1).pack(pady =5)
68#
69# class Demo(Frame):
70#
"Dmo. de quelques caractristiques du widget Toplevel"
71#
def __init__(self):
72#
Frame.__init__(self)
73#
self.master.geometry("400x300+200+200")
74#
self.master.config(bg ="cadet blue")
75#
FunnyButton(self, text ="Top 1", command =self.top1).pack(side =LEFT)
76#
FunnyButton(self, text ="Top 2", command =self.top2).pack(side =LEFT)
77#
FunnyButton(self, text ="Quitter", command =self.quit).pack()
78#
self.pack(side =BOTTOM, padx =10, pady =10)
79#
80#
def top1(self):
81#
self.fen1 =FenDessin(bg ="grey")
82#
83#
def top2(self):
84#
self.fen2 =FenControle(self, bg ="khaki")
85#
86#
def redimF1(self):
87#
dimX, dimY = self.fen2.spX.get(), self.fen2.spY.get()
88#
self.fen1.can.config(width =dimX, height =dimY)

238
89#
90# if __name__ =="__main__":
91#
Demo().mainloop()

Et pour quelques widgets de plus...


# --- Programme de test ---

Commentaires
Lignes 3 8 : si vous souhaitez pouvoir disposer du mme style de boutons diffrents endroits de
votre projet, nhsitez pas crer une classe drive, comme nous lavons fait ici. Cela vous vitera
davoir reprogrammer partout les mmes options spcifiques.
Notez bien les deux astrisques qui prfixent le nom du dernier paramtre du constructeur : **Arguments. Elles signifient que la variable correspondante sera en fait un dictionnaire, capable de
rceptionner automatiquement nimporte quel ensemble darguments avec tiquettes. Ces
arguments pourront alors tre transmis tels quels au constructeur de la classe parente ( la ligne 8
). Cela nous vite davoir reproduire dans notre liste de paramtres toutes les options de
paramtrage du bouton de base, qui sont fort nombreuses. Ainsi, vous pourrez instancier ces
boutons fantaisie avec nimporte quelle combinaison doptions, du moment que celles-ci sont
disponibles pour les boutons de base. On appelle ce qui prcde un paramtrage implicite. Vous
pouvez utiliser cette forme de paramtrage avec nimporte quelle fonction ou mthode.
Lignes 10 24 : le constructeur de notre widget SpinBox ne ncessite gure de commentaires. En
fonction de lorientation souhaite, la mthode pack() disposera les boutons et le libell de haut
en bas ou de gauche droite (arguments TOP ou RIGHT pour son option side).
Lignes 26 38 : ces deux mthodes ne font rien dautre que de modifier la valeur affiche dans le
libell. Notez au passage que la classe Frame() dispose dune mthode bell() pour provoquer
lmission dun bip sonore.
Lignes 43 53 : la premire fentre satellite est dfinie ici. Remarquez nouveau lutilisation du
paramtrage implicite du constructeur, laide de la variable **Arguments. Cest lui qui nous
permet dinstancier cette fentre ( la ligne 81) en spcifiant une couleur de fond (nous pourrions
aussi demander une bordure, etc.). Les mthodes invoques aux lignes 47 49 dfinissent quelques
proprits particulires (applicables nimporte quelle fentre). La mthode geometry() permet
de fixer la fois les dimensions de la fentre et son emplacement lcran (lindication +100+240
signifie que son coin suprieur gauche doit tre dcal de 100 pixels vers la droite, et de 240 pixels
vers le bas, par rapport au coin suprieur gauche de lcran).
Lignes 45 et 57 : veuillez remarquer la petite diffrence entre les listes de paramtres de ces lignes.
Dans le constructeur de FenDessin, nous avons omis le paramtre boss, qui est bien prsent dans
le constructeur de FenControle. Vous savez que ce paramtre sert transmettre la rfrence du
widget matre son esclave . Il est trs gnralement ncessaire ( la ligne 67, par exemple,
nous nous en servons pour rfrencer une mthode de lapplication principale), mais il nest pas
absolument indispensable : dans FenDessin nous nen avons aucune utilit. Vous retrouverez bien
videmment la diffrence correspondante dans les instructions dinstanciation de ces fentres,
aux lignes 82 et 84.
Lignes 55 67 : lexception de la diffrence mentionne ci-dessus, la construction du widget FenControle est trs similaire celle de FenDessin. Remarquez la ligne 60 la mthode permettant
dempcher le redimensionnement dune fentre (dans le cas dune fentre sans bordure ni
bandeau-titre, comme FenDessin, cette mthode est inutile).
Lignes 73-74 (et 49) : toutes les classes drives des widgets tkinter sont dotes automatiquement
dun attribut master, qui contient la rfrence de la classe parente. Cest ce qui nous permet ici
daccder aux dimensions et la couleur de fond de la fentre matresse.
Lignes 86 88 : cette mthode rcupre les valeurs numriques affiches dans la fentre de
contrle (mthode get() de ce widget), pour redimensionner le canevas de la fentre de dessin. Cet

Application fentres multiples paramtrage implicite

239

exemple simple vous montre, une fois de plus, comment stablissent les communications entre
divers composants de votre programme.

Barres doutils expressions lambda


De nombreux programmes comportent une ou plusieurs barres doutils (toolbar) constitues de
petits boutons sur lesquels sont reprsents des pictogrammes (icnes). Cette faon de faire permet de
proposer lutilisateur un grand nombre de commandes spcialises, sans que celles-ci noccupent
une place excessive lcran.

Lapplication dcrite ci-aprs comporte une barre doutils et un canevas. Lorsque lutilisateur clique
sur lun des 8 premiers boutons de la barre, le pictogramme quil porte est recopi dans le canevas,
un emplacement choisi au hasard. Lorsquil clique sur le dernier bouton, le contenu du canevas est
effac.
1# from tkinter import *
2# from random import randrange
3#
4# class ToolBar(Frame):
5#
"Barre d'outils (petits boutons avec icnes)"
6#
def __init__(self, boss, images =[], command =None, **Arguments):
7#
Frame.__init__(self, boss, bd =1, **Arguments)
8#
# <images> = liste des noms d'icnes placer sur les boutons
9#
self.command =command
# commande excuter lors du clic
10#
nBou =len(images)
# Nombre de boutons construire
11#
# Les icnes des boutons doivent tre places dans des variables
12#
# persistantes. Une liste fera l'affaire :
13#
self.photoI =[None]*nBou
14#
for b in range(nBou):
15#
# Cration de l'icne (objet PhotoImage Tkinter) :
16#
self.photoI[b] =PhotoImage(file = images[b] +'.gif')
17#
# Cration du bouton. On fait appel une fonction lambda
18#
# pour pouvoir transmettre un argument la mthode <action> :
19#
bou = Button(self, image =self.photoI[b], bd =2, relief =GROOVE,
20#
command = lambda arg =b: self.action(arg))
21#
bou.pack(side =LEFT)
22#

240

Et pour quelques widgets de plus...

23#
def action(self, index):
24#
# Excuter <command> avec l'indice du bouton comme argument :
25#
self.command(index)
26#
27# class Application(Frame):
28#
def __init__(self):
29#
Frame.__init__(self)
30#
# noms des fichiers contenant les icones (format GIF):
31#
icones =('floppy_2', 'coleo', 'papi2', 'pion_1', 'pion_2', 'pion_3',
32#
'pion_4', 'help_4', 'clear')
33#
# Cration de la barre d'outils :
34#
self.barOut =ToolBar(self, images =icones, command =self.transfert)
35#
self.barOut.pack(expand =YES, fill =X)
36#
# Cration du canevas destin recevoir les images :
37#
self.ca = Canvas(self, width =400, height =200, bg ='orange')
38#
self.ca.pack()
39#
self.pack()
40#
41#
def transfert(self, b):
42#
if b ==8:
43#
self.ca.delete(ALL)
# Effacer tout dans le canevas
44#
else:
45#
# Recopier l'icne du bouton b (extraite de la barre) => canevas :
46#
x, y = randrange(25,375), randrange(25,175)
47#
self.ca.create_image(x, y, image =self.barOut.photoI[b])
48#
49# Application().mainloop()

Mtaprogrammation expressions lambda


Vous savez quen rgle gnrale, on associe chaque bouton une commande, laquelle est la rfrence
dune mthode ou dune fonction particulire qui se charge deffectuer le travail lorsque le bouton est
activ. Or, dans lapplication prsente, tous les boutons doivent faire peu prs la mme chose
(recopier un dessin dans le canevas), la seule diffrence entre eux tant le dessin concern.
Pour simplifier notre code, nous voudrions donc pouvoir associer loption command de tous nos
boutons avec une seule et mme mthode (ce sera la mthode action() ), mais en lui transmettant
chaque fois la rfrence du bouton particulier utilis, de manire ce que laction accomplie puisse
tre diffrente pour chacun deux.
Une difficult se prsente, cependant, parce que loption command du widget Button accepte
seulement une valeur ou une expression, et non une instruction. Il sagit en fait de lui indiquer la
rfrence dune fonction, mais certainement pas dinvoquer la fonction cet endroit avec des
arguments (linvocation ne pourra avoir lieu en effet que lorsque lutilisateur cliquera sur le bouton :
cest alors le rceptionnaire dvnements de tkinter qui devra la provoquer). Cest la raison pour
laquelle on indique seulement le nom de la fonction, sans parenthses.
On peut rsoudre cette difficult de deux manires :

Du fait de son caractre dynamique, Python accepte quun programme puisse se modifier lui-mme,
par exemple en dfinissant de nouvelles fonctions au cours de son excution (cest le concept de
mtaprogrammation).
Il est donc possible de dfinir la vole une fonction avec des paramtres, en indiquant pour chacun
de ceux-ci une valeur par dfaut, et de fournir ensuite la rfrence de cette fonction loption com-

Barres doutils expressions lambda

241

mand. Puisque la fonction est dfinie en cours dexcution, ces valeurs par dfaut peuvent tre les
contenus de variables. Lorsque lvnement clic sur le bouton provoquera lappel de la fonction, celle-ci utilisera donc les valeurs par dfaut de ses paramtres, comme sil sagissait darguments. Le rsultat de lopration quivaut par consquent un transfert darguments classique.
Pour illustrer cette technique, remplacez les lignes 17 20 du script par les suivantes :
# Cration du bouton. On dfinit la vole une fonction avec un
# paramtre dont la valeur par dfaut est l'argument transmettre.
# Cette fonction appelle la mthode qui ncessite un argument :

def agir(arg = b):


self.action(arg)

# La commande associe au bouton appelle la fonction ci-dessus :

bou = Button(self, image = self.photoI[b], relief = GROOVE,


command = agir)

Tout ce qui prcde peut tre simplifi, en faisant appel une expression lambda. Ce mot rserv
Python dsigne une expression qui renvoie un objet fonction, similaire ceux que vous crez avec
linstruction def, mais avec la diffrence que lambda tant une expression et non une instruction,
on peut lutiliser comme interface afin dinvoquer une fonction (avec passage darguments) l o
ce nest normalement pas possible. Notez au passage quune telle fonction est anonyme (elle ne
possde pas de nom).
Par exemple, lexpression :
lambda ar1=b, ar2=c : bidule(ar1,ar2)

renvoie la rfrence dune fonction anonyme cre la vole, qui pourra elle-mme invoquer la
fonction bidule() en lui transmettant les arguments b et c, ceux-ci tant utiliss comme valeurs
par dfaut dans la dfinition des paramtres ar1 et ar2 de la fonction.
Cette technique utilise finalement le mme principe que la prcdente, mais elle prsente lavantage dtre plus concise, raison pour laquelle nous lavons utilise dans notre script. En revanche,
elle est un peu plus difficile comprendre :
command = lambda arg =b: self.action(arg)

Dans cette portion dinstruction, la commande associe au bouton se rfre une fonction
anonyme dont le paramtre arg possde une valeur par dfaut : la valeur de largument b.
Invoque sans argument par la commande, cette fonction anonyme peut tout de mme utiliser son
paramtre arg (avec la valeur par dfaut) pour faire appel la mthode cible self.action(), et lon
obtient ainsi un vritable transfert dargument vers cette mthode.
Nous ne dtaillerons pas davantage ici la question des expressions lambda, car elle dborde du cadre
que nous nous sommes fix pour cet ouvrage dinitiation. Si vous souhaitez en savoir plus, veuillez
donc consulter lun ou lautre des ouvrages de rfrence cits dans la bibliographie.

Passage dune fonction (ou dune mthode) comme argument


Vous avez dj rencontr de nombreux widgets comportant une telle option command, laquelle il
faut chaque fois associer le nom dune fonction ou dune mthode. En termes plus gnraux, cela
signifie donc quune fonction avec paramtres peut recevoir la rfrence dune autre fonction comme
argument, et lutilit de la chose apparat clairement ici.

242

Et pour quelques widgets de plus...

Nous avons dailleurs programm nous-mmes une fonctionnalit de ce genre dans notre nouvelle
classe ToolBar(). Vous constatez que nous avons inclus le nom command dans la liste de paramtres
de son constructeur, la ligne 6. Ce paramtre attend la rfrence dune fonction ou dune mthode
comme argument. La rfrence est alors mmorise dans une variable dinstance ( la ligne 9), de
manire tre accessible depuis les autres mthodes de la classe. Celles-ci peuvent ds lors invoquer la
fonction ou la mthode (au besoin en lui transmettant des arguments si ncessaire, suivant la
technique explique la rubrique prcdente). Cest ce que fait donc notre mthode action(), la
ligne 25. En loccurrence, la mthode ainsi transmise est la mthode transfert() de la classe Application (cf. ligne 34).
Le rsultat de cette dmarche est que nous sommes parvenus ainsi dvelopper une classe dobjets
ToolBar parfaitement rutilisables dans dautres contextes. Comme le montre notre petite
application, il suffit en effet dinstancier ces objets en indiquant la rfrence dune fonction
quelconque en argument de loption command. Cette fonction sera alors automatiquement appele
elle-mme avec le numro dordre du bouton sur lequel lutilisateur a cliqu.
Libre vous dimaginer prsent ce que la fonction devra effectuer !
Pour en terminer avec cet exemple, remarquons encore un petit dtail : chacun de nos boutons
apparat entour dun sillon (option relief =GROOVE). Vous pouvez aisment obtenir dautres aspects
en choisissant judicieusement les options relief et bd (bordure) dans linstruction dinstanciation de
ces boutons. En particulier, vous pouvez choisir relief =FLAT et bd =0 pour obtenir des petits
boutons plats , sans aucun relief.

Fentres avec menus


Pour terminer notre petite visite guide des
widgets tkinter, nous allons dcrire prsent la
construction dune fentre dapplication dote
de diffrents types de menus droulants ,
chacun de ces menus pouvant tre dtach
de lapplication principale pour devenir
lui-mme une petite fentre indpendante,
comme sur lillustration ci-contre.
Cet exercice un peu plus long nous servira
galement de rvision, et nous le raliserons par
tapes, en appliquant une stratgie de
programmation que lon appelle dveloppement
incrmental.
Comme
nous
lavons
dj
expliqu
prcdemment86, cette mthode consiste
commencer lcriture dun programme par une
bauche, qui ne comporte que quelques lignes
86 Voir page

8 : recherche des erreurs et exprimentation.

Fentres avec menus

243

seulement mais qui est dj fonctionnelle. On teste alors cette bauche soigneusement afin den
liminer les bugs ventuels. Lorsque lbauche fonctionne correctement, on y ajoute une
fonctionnalit supplmentaire. On teste ce complment jusqu ce quil donne entire satisfaction,
puis on en ajoute un autre, et ainsi de suite...
Cela ne signifie pas que vous pouvez commencer directement programmer sans avoir au pralable effectu
une analyse srieuse du projet, dont au moins les grandes lignes devront tre convenablement
dcrites dans un cahier des charges clairement rdig.
Il reste galement impratif de commenter convenablement le code produit, au fur et
mesure de son laboration. Sefforcer de rdiger de bons commentaires est en effet
ncessaire, non seulement pour que votre code soit facile lire (et donc maintenir plus
tard, par dautres ou par vous-mme), mais aussi pour que vous soyez forcs d exprimer
ce que vous souhaitez vraiment que la machine fasse (cf. erreurs smantiques, page 7.)

Cahier des charges de lexercice


Notre application comportera simplement une barre de menus et un canevas. Les diffrentes
rubriques et options des menus ne serviront qu faire apparatre des fragments de texte dans le
canevas ou modifier des dtails de dcoration, mais ce seront avant tout des exemples varis,
destins donner un aperu des nombreuses possibilits offertes par ce type de widget, accessoire
indispensable de toute application moderne dune certaine importance.
Nous souhaitons galement que le code produit dans cet exercice soit bien structur. Pour ce faire,
nous ferons usage de deux classes : une classe pour lapplication principale, et une autre pour la barre
de menus. Nous voulons procder ainsi afin de bien mettre en vidence la construction dune
application type incorporant plusieurs classes dobjets interactifs.

Premire bauche du programme


Lorsque lon construit lbauche dun programme, il faut tcher dy faire apparatre le plus tt possible
la structure densemble, avec les relations entre les principaux blocs qui constitueront lapplication
dfinitive. Cest ce que nous nous sommes efforcs de faire dans lexemple ci-aprs.
1# from tkinter import *
2#
3# class MenuBar(Frame):
4#
"""Barre de menus droulants"""
5#
def __init__(self, boss =None):
6#
Frame.__init__(self, borderwidth =2)
7#
8#
##### Menu <Fichier> #####
9#
fileMenu = Menubutton(self, text ='Fichier')
10#
fileMenu.pack(side =LEFT)
11#
# Partie "droulante" :
12#
me1 = Menu(fileMenu)
13#
me1.add_command(label ='Effacer', underline =0,
14#
command = boss.effacer)
15#
me1.add_command(label ='Terminer', underline =0,
16#
command = boss.quit)
17#
# Intgration du menu :
18#
fileMenu.configure(menu = me1)
19#
20# class Application(Frame):

244

Et pour quelques widgets de plus...

21#
"""Application principale"""
22#
def __init__(self, boss =None):
23#
Frame.__init__(self)
24#
self.master.title('Fentre avec menus')
25#
mBar = MenuBar(self)
26#
mBar.pack()
27#
self.can = Canvas(self, bg='light grey', height=190,
28#
width=250, borderwidth =2)
29#
self.can.pack()
30#
self.pack()
31#
32#
def effacer(self):
33#
self.can.delete(ALL)
34#
35# if __name__ == '__main__':
36#
app = Application()
37#
app.mainloop()

Veuillez donc encoder ces lignes et en tester lexcution. Vous devriez obtenir une fentre avec un canevas
gris clair surmont dune barre de menus. ce stade, la
barre de menus ne comporte encore que la seule
rubrique Fichier .
Cliquez sur la rubrique Fichier pour faire apparatre
le menu correspondant : loption Effacer nest pas
encore fonctionnelle (elle servira plus loin effacer le
contenu du canevas), mais loption Terminer
devrait dj vous permettre de fermer proprement
lapplication.
Comme tous les menus grs par tkinter, le menu que
vous avez cr peut tre converti en menu flottant :
il suffit de cliquer sur la ligne pointille apparaissant
en-tte de menu. Vous obtenez ainsi une petite fentre
satellite, que vous pouvez alors positionner o bon vous semble sur le bureau.
Analyse du script
La structure de ce petit programme devrait vous apparatre familire : afin que les classes dfinies
dans ce script puissent ventuellement tre (r)utilises dans dautres projets par importation, comme
nous lavons dj expliqu prcdemment87, le corps principal du programme (lignes 35 37)
comporte linstruction dsormais classique : if __name__ == __main__:
Les deux instructions qui suivent consistent seulement instancier un objet app et faire fonctionner
sa mthode mainloop(). Comme vous le savez certainement, nous aurions pu galement condenser ces
deux instructions en une seule.
Lessentiel du programme se trouve cependant dans les dfinitions de classes qui prcdent.
La classe MenuBar() contient la description de la barre de menus. Dans ltat prsent du script, elle se
rsume une bauche de constructeur.
87 Voir page

188 : modules contenant des bibliothques de classes.

Fentres avec menus

245

Ligne 5 : Le paramtre boss rceptionne la rfrence de la fentre matresse du widget au moment


de son instanciation. Cette rfrence va nous permettre dinvoquer les mthodes associes cette
fentre matresse, aux lignes 14 et 16.
Ligne 6 : Activation obligatoire du constructeur de la classe parente.
Ligne 9 : Instanciation dun widget de la classe Menubutton(), dfini comme un esclave de self
(cest--dire lobjet composite barre de menus dont nous sommes occups dfinir la classe).
Comme lindique son nom, ce type de widget se comporte un peu comme un bouton : une action se
produit lorsque lon clique dessus.
Ligne 12 : Afin que cette action consiste en lapparition vritable dun menu, il reste encore
dfinir celui-ci : ce sera encore un nouveau widget, de la classe Menu() cette fois, dfini lui-mme
comme un esclave du widget Menubutton instanci la ligne 9.
Lignes 13 16 : On peut appliquer aux widgets de la classe Menu() un certain nombre de mthodes
spcifiques, chacune delles acceptant de nombreuses options. Nous utilisons ici la mthode
add_command() pour installer dans le menu les deux items Effacer et Terminer . Nous y
intgrons tout de suite loption underline, qui sert dfinir un raccourci clavier : cette option
indique en effet lequel des caractres de litem doit apparatre soulign lcran. Lutilisateur sait
alors quil lui suffit de frapper ce caractre au clavier pour que laction correspondant cet item
soit active (comme sil avait cliqu dessus laide de la souris).
Laction dclencher lorsque lutilisateur slectionne litem est dsigne par loption command.
Dans notre script, les commandes invoques sont toutes les deux des mthodes de la fentre
matresse, dont la rfrence aura t transmise au prsent widget au moment de son instanciation
par lintermdiaire du paramtre boss. La mthode effacer(), que nous dfinissons nous-mme
plus loin, servira vider le canevas. La mthode prdfinie quit() provoque la sortie de la boucle
mainloop() et donc larrt du rceptionnaire dvnements associ la fentre dapplication.
Ligne 18 : Lorsque les items du menu ont t dfinis, il reste encore reconfigurer le widget
matre Menubutton de manire ce que son option menu dsigne effectivement le Menu que
nous venons de construire. En effet, nous ne pouvions pas dj prciser cette option lors de la
dfinition initiale du widget Menubutton, puisqu ce stade le Menu nexistait pas encore. Nous
ne pouvions pas non plus dfinir le widget Menu en premier lieu, puisque celui-ci doit tre dfini
comme un esclave du widget Menubutton. Il faut donc bien procder en trois tapes comme
nous lavons fait, en faisant appel la mthode configure(). Cette mthode peut tre applique
nimporte quel widget prexistant pour en modifier lune ou lautre option.
La classe Application() contient la description de la fentre principale du programme ainsi que les
mthodes gestionnaires dvnements qui lui sont associes.
Ligne 20 : Nous prfrons faire driver notre application de la classe Frame(), qui prsente de
nombreuses options, plutt que de la classe primordiale Tk(). De cette manire, lapplication toute
entire est encapsule dans un widget, lequel pourra ventuellement tre intgr par la suite dans
une application plus importante. Rappelons que, de toute manire, tkinter instanciera
automatiquement une fentre matresse de type Tk() pour contenir cette Frame.
Lignes 23-24 : Aprs lindispensable activation du constructeur de la classe parente, nous utilisons
lattribut master que tkinter associe automatiquement chaque widget, pour rfrencer la classe

246

Et pour quelques widgets de plus...

parente (dans le cas prsent, lobjet correspondant est la fentre principale de lapplication) et en
redfinir le bandeau-titre.
Lignes 25 29 : Instanciation de deux widgets esclaves pour notre cadre (Frame) principal. La
barre de menus est videmment le widget dfini dans lautre classe.
Ligne 30 : Comme nimporte quel autre widget, notre cadre principal doit tre confi une
mthode de mise en place afin dapparatre vritablement.
Lignes 32-33 : La mthode servant effacer le canevas est dfinie dans la classe prsente (puisque
lobjet canevas en fait partie), mais elle est invoque par loption command dun widget esclave
dfini dans une autre classe.
Comme nous lavons expliqu plus haut, ce widget esclave reoit la rfrence de son widget matre
par lintermdiaire du paramtre boss. Toutes ces rfrences sont hirarchises laide de la
qualification des noms par points.

Ajout de la rubrique Musiciens


Continuez le dveloppement de ce petit programme, en ajoutant les lignes suivantes dans le
constructeur de la classe MenuBar() (aprs la ligne 18) :
##### Menu <Musiciens> #####
self.musi = Menubutton(self, text ='Musiciens')
self.musi.pack(side =LEFT, padx ='3')
# Partie "droulante" du menu <Musiciens> :
me1 = Menu(self.musi)
me1.add_command(label ='17e sicle', underline =1,
foreground ='red', background ='yellow',
font =('Comic Sans MS', 11),
command = boss.showMusi17)
me1.add_command(label ='18e sicle', underline =1,
foreground='royal blue', background ='white',
font =('Comic Sans MS', 11, 'bold'),
command = boss.showMusi18)
# Intgration du menu :
self.musi.configure(menu = me1)

... ainsi que les dfinitions de mthodes suivantes la classe Application() (aprs la ligne 33) :
def showMusi17(self):
self.can.create_text(10, 10, anchor =NW, text ='H. Purcell',
font=('Times', 20, 'bold'), fill ='yellow')
def showMusi18(self):
self.can.create_text(245, 40, anchor =NE, text ="W. A. Mozart",
font =('Times', 20, 'italic'), fill ='dark green')

Fentres avec menus

247

Lorsque vous y aurez ajout toutes ces lignes,


sauvegardez le script et excutez-le.
Votre barre de menus comporte prsent une rubrique
supplmentaire : la rubrique Musiciens .
Le menu correspondant propose deux items qui sont
affichs avec des couleurs et des polices personnalises.
Vous pourrez vous inspirer de ces techniques
dcoratives pour vos projets personnels. utiliser avec
modration !
Les commandes que nous avons associes ces items
sont videmment simplifies afin de ne pas alourdir
lexercice : elles provoquent laffichage de petits textes
sur le canevas.
Analyse du script
Les seules nouveauts introduites dans ces lignes concernent lutilisation de polices de caractres bien
dtermines (option font), ainsi que de couleurs pour lavant-plan (option foreground) et le fond
(option background) des textes affichs.
Veuillez noter encore une fois lutilisation de loption underline pour dsigner les caractres
correspondant des raccourcis claviers (en noubliant pas que la numrotation des caractres dune
chane commence partir de zro), et surtout que loption command de ces widgets accde aux
mthodes de lautre classe, par lintermdiaire de la rfrence mmorise dans lattribut boss.
La mthode create_text() du canevas doit tre utilise avec deux arguments numriques, qui sont les
coordonnes X et Y dun point dans le canevas. Le texte transmis sera positionn par rapport ce
point, en fonction de la valeur choisie pour loption anchor : celle-ci dtermine comment le fragment
de texte doit tre ancr au point choisi dans le canevas, par son centre, par son coin suprieur
gauche, etc., en fonction dune syntaxe qui utilise lanalogie des points cardinaux gographiques (NW
= angle suprieur gauche, SE = angle infrieur droit, CENTER = centre, etc.).

Ajout de la rubrique Peintres


Cette nouvelle rubrique est construite dune manire assez semblable la prcdente, mais nous lui
avons ajout une fonctionnalit supplmentaire : des menus en cascade . Veuillez donc ajouter les
lignes suivantes dans le constructeur de la classe MenuBar() :
##### Menu <Peintres> #####
self.pein = Menubutton(self, text ='Peintres')
self.pein.pack(side =LEFT, padx='3')
# Partie "droulante" :
me1 = Menu(self.pein)
me1.add_command(label ='classiques', state=DISABLED)
me1.add_command(label ='romantiques', underline =0,
command = boss.showRomanti)
# Sous-menu pour les peintres impressionistes :
me2 = Menu(me1)

248

Et pour quelques widgets de plus...


me2.add_command(label ='Claude Monet', underline =7,
command = boss.tabMonet)
me2.add_command(label ='Auguste Renoir', underline =8,
command = boss.tabRenoir)
me2.add_command(label ='Edgar Degas', underline =6,
command = boss.tabDegas)
# Intgration du sous-menu :
me1.add_cascade(label ='impressionistes', underline=0, menu =me2)
# Intgration du menu :
self.pein.configure(menu =me1)

... et les dfinitions suivantes dans la classe Application() :


def showRomanti(self):
self.can.create_text(245, 70, anchor =NE, text = "E. Delacroix",
font =('Times', 20, 'bold italic'), fill ='blue')
def tabMonet(self):
self.can.create_text(10, 100, anchor =NW, text = 'Nymphas Giverny',
font =('Technical', 20), fill ='red')
def tabRenoir(self):
self.can.create_text(10, 130, anchor =NW,
text = 'Le moulin de la galette',
font =('Dom Casual BT', 20), fill ='maroon')
def tabDegas(self):
self.can.create_text(10, 160, anchor =NW, text = 'Danseuses au repos',
font =('President', 20), fill ='purple')

Analyse du script
Vous pouvez raliser aisment des menus en cascade, en enchanant des sous-menus les uns aux
autres jusqu un niveau quelconque (il vous est cependant dconseill daller au-del de 5 niveaux
successifs : vos utilisateurs sy perdraient).
Un sous-menu est dfini comme un menu esclave du menu de niveau prcdent (dans notre
exemple, me2 est dfini comme un menu esclave de me1). Lintgration est assure ensuite laide
de la mthode add_cascade().
Lun des items est dsactiv (option state = DISABLED). Lexemple suivant vous montrera comment
vous pouvez activer ou dsactiver volont des items, par programme.

Ajout de la rubrique Options


La dfinition de cette rubrique est un peu plus complique, parce que nous allons y intgrer l utilisation de variables internes tkinter.

Fentres avec menus

249

Les fonctionnalits de ce menu sont cependant beaucoup plus


labores : les options ajoutes permettent en effet dactiver ou de
dsactiver volont les rubriques Musiciens et Peintres , et
vous pouvez galement modifier volont laspect de la barre de
menus elle-mme.
Veuillez donc ajouter les lignes suivantes dans le constructeur de
la classe MenuBar() :
##### Menu <Options> #####
optMenu = Menubutton(self, text ='Options')
optMenu.pack(side =LEFT, padx ='3')
# Variables tkinter :
self.relief = IntVar()
self.actPein = IntVar()
self.actMusi = IntVar()
# Partie "droulante" du menu :
self.mo = Menu(optMenu)
self.mo.add_command(label = 'Activer :', foreground ='blue')
self.mo.add_checkbutton(label ='musiciens',
command = self.choixActifs, variable =self.actMusi)
self.mo.add_checkbutton(label ='peintres',
command = self.choixActifs, variable =self.actPein)
self.mo.add_separator()
self.mo.add_command(label = 'Relief :', foreground ='blue')
for (v, lab) in [(0,'aucun'), (1,'sorti'), (2,'rentr'),
(3,'sillon'), (4,'crte'), (5,'bordure')]:
self.mo.add_radiobutton(label =lab, variable =self.relief,
value =v, command =self.reliefBarre)
# Intgration du menu :
optMenu.configure(menu = self.mo)

... ainsi que les dfinitions de mthodes suivantes (toujours dans la classe MenuBar()) :
def reliefBarre(self):
choix = self.relief.get()
self.configure(relief =[FLAT,RAISED,SUNKEN,GROOVE,RIDGE,SOLID][choix])
def choixActifs(self):
p = self.actPein.get()
m = self.actMusi.get()
self.pein.configure(state =[DISABLED, NORMAL][p])
self.musi.configure(state =[DISABLED, NORMAL][m])

Menu avec cases cocher


Notre nouveau menu droulant comporte deux parties. Afin de bien les mettre en vidence, nous
avons insr une ligne de sparation ainsi que deux faux items ( Activer : et Relief : ) qui
servent simplement de titres. Nous faisons apparatre ceux-ci en couleur pour que lutilisateur ne les
confonde pas avec de vritables commandes.
Les items de la premire partie sont dotes de cases cocher . Lorsque lutilisateur effectue un clic
de souris sur lun ou lautre de ces items, les options correspondantes sont actives ou dsactives, et
ces tats actif / inactif sont affichs sous la forme dune encoche. Les instructions qui servent
mettre en place ce type de rubrique sont assez explicites. Elles prsentent en effet ces items comme
des widgets de type chekbutton :

250

Et pour quelques widgets de plus...


self.mo.add_checkbutton(label = 'musiciens', command = choixActifs,
variable = mbu.me1.music)

Il est important de comprendre ici que ce type de widget comporte ncessairement une variable
interne, destine mmoriser ltat actif / inactif du widget. Comme nous lavons dj expliqu
plus haut, cette variable ne peut pas tre une variable Python ordinaire, parce que les classes de la
bibliothque tkinter sont crites dans un autre langage. Et par consquent, on ne pourra accder une
telle variable interne qu travers un objet-interface, que nous appellerons variable tkinter pour
simplifier88.
Cest ainsi que dans notre exemple, nous utilisons la classe tkinter IntVar() pour crer des objets
quivalents des variables de type entier.

Nous instancions donc un de ces objets-variables, que nous mmorisons comme attribut dinstance : self.actMusi =IntVar().
Aprs cette affectation, lobjet rfrenc dans self.actMusi contient dsormais lquivalent dune
variable de type entier, dans un format spcifique tkinter.
Il faut ensuite associer loption variable de lobjet checkbutton la variable tkinter ainsi dfinie :
self.mo.add_checkbutton(label =musiciens, variable =self.actMusi).
Il est ncessaire de procder ainsi en deux tapes, parce que tkinter ne peut pas directement
assigner des valeurs aux variables Python. Pour une raison similaire, il nest pas possible Python
de lire directement le contenu dune variable tkinter. Il faut utiliser pour cela les mthodes
spcifiques de cette classe dobjets : la mthode get() pour lire, et la mthode set() pour crire :
m = self.actMusi.get().
Dans cette instruction, nous affectons m (variable ordinaire de Python) le contenu de la variable
tkinter self.actMusi (laquelle est elle-mme associe un widget bien dtermin).
Tout ce qui prcde peut vous paratre un peu compliqu. Considrez simplement quil sagit de votre
premire rencontre avec les problmes dinterfaage entre deux langages de programmation
diffrents, utiliss ensemble dans un projet composite.

Menu avec choix exclusifs


La deuxime partie du menu Options permet lutilisateur de choisir laspect que prendra la barre
de menus, parmi six possibilits. Il va de soi que lon ne peut activer quune seule de ces possibilits
la fois. Pour mettre en place ce genre de fonctionnalit, on fait classiquement appel appel des
widgets de type boutons radio . La caractristique essentielle de ces widgets est que plusieurs
dentre eux doivent tre associs une seule et mme variable tkinter. chaque bouton radio
correspond alors une valeur particulire, et cest cette valeur qui est affecte la variable lorsque
lutilisateur slectionne le bouton.
Ainsi, linstruction :
self.mo.add_radiobutton(label ='sillon', variable =self.relief,
value =3, command =self.reliefBarre)

configure un item du menu Options de telle manire quil se comporte comme un bouton radio.
88

Voir galement page 206.

Fentres avec menus

251

Lorsque lutilisateur slectionne cet item, la valeur 3 est affecte la variable tkinter self.relief
(celle-ci tant dsigne laide de loption variable du widget), et un appel est lanc en direction de la
mthode reliefBarre(). Celle-ci rcupre alors la valeur mmorise dans la variable tkinter pour
effectuer son travail.
Dans le contexte particulier de ce menu, nous souhaitons proposer 6 possibilits diffrentes lutilisateur. Il nous faut donc six boutons radio , pour lesquels nous pourrions encoder six instructions
similaires celle que nous avons reproduite ci-dessus, chacune delles ne diffrant des cinq autres que
par ses options value et label. Dans une situation de ce genre, la bonne pratique de programmation
consiste placer les valeurs de ces options dans une liste, et parcourir ensuite cette liste laide
dune boucle for, afin dinstancier les widgets avec une instruction commune :
for (v, lab) in [(0,'aucun'), (1,'sorti'), (2,'rentr'),
(3,'sillon'), (4,'crte'), (5,'bordure')]:
self.mo.add_radiobutton(label =lab, variable =self.relief,
value =v, command =self.reliefBarre)

La liste utilise est une liste de 6 tuples (valeur, libell). chacune des 6 itrations de la boucle, un
nouvel item radiobutton est instanci, dont les options label et value sont extraites de la liste par
lintermdiaire des variables lab et v.
Dans vos projets personnels, il vous arrivera frquemment de constater que vous pouvez ainsi
remplacer des suites dinstructions similaires par une structure de programmation plus compacte (en
gnral, la combinaison dune liste et dune boucle, comme dans lexemple ci-dessus).
Vous dcouvrirez petit petit encore dautres techniques pour allger votre code : nous en
fournissons un exemple dans le paragraphe suivant. Tchez cependant de garder l 'esprit cette rgle
essentielle : un bon programme doit avant tout rester trs lisible et bien comment.

Contrle du flux dexcution laide dune liste


Veuillez prsent considrer la dfinition de la mthode reliefBarre().
la premire ligne, la mthode get() nous permet de rcuprer ltat dune variable tkinter qui
contient le numro du choix opr par lutilisateur dans le sous-menu Relief : .
la seconde ligne, nous utilisons le contenu de la variable choix pour extraire dune liste de six
lments celui qui nous intresse. Par exemple, si choix contient la valeur 2, cest loption SUNKEN
qui sera utilise pour reconfigurer le widget.
La variable choix est donc utilise ici comme un index, servant dsigner un lment de la liste. En
lieu et place de cette construction compacte, nous aurions pu programmer une srie de tests
conditionnels, comme :
if choix ==0:
self.configure(relief =FLAT)
elif choix ==1:
self.configure(relief =RAISED)
elif choix ==2:
self.configure(relief =SUNKEN)
...
etc.

252

Et pour quelques widgets de plus...

Dun point de vue strictement fonctionnel, le rsultat serait exactement le mme. Vous admettrez
cependant que la construction que nous avons choisie est dautant plus efficace que le nombre de
possibilits de choix est lev. Imaginez par exemple que lun de vos programmes personnels doive
effectuer une slection dans un trs grand nombre dlments : avec une construction du type
ci-dessus, vous seriez peut-tre amen encoder plusieurs pages de elif !
Nous utilisons encore la mme technique dans la mthode choixActifs(). Ainsi linstruction :
self.pein.configure(state =[DISABLED, NORMAL][p])

utilise le contenu de la variable p comme index pour dsigner lequel des deux tats DISABLED, NORMAL doit tre slectionn pour reconfigurer le menu Peintres .
Lorsquelle est appele, la mthode choixActifs() reconfigure donc les deux rubriques Peintres et
Musiciens de la barre de menus, pour les faire apparatre normales ou dsactives en
fonction de ltat des variables m et p, lesquelles sont elles-mmes le reflet de variables tkinter.
Ces variables intermdiaires m et p ne servent en fait qu clarifier le script. Il serait en effet
parfaitement possible de les liminer, et de rendre le script encore plus compact, en utilisant la
composition dinstructions. On pourrait par exemple remplacer les deux instructions :
m = self.actMusi.get()
self.musi.configure(state =[DISABLED, NORMAL][m])

par une seule, telle que :


self.musi.configure(state =[DISABLED, NORMAL][self.actMusi.get()])

Notez cependant que ce que lon gagne en compacit peut se payer dune certaine perte de lisibilit.

Prslection dune rubrique


Pour terminer cet exercice, voyons encore comment vous pouvez dterminer lavance certaines
slections, ou bien les modifier par programme.
Veuillez donc ajouter linstruction suivante dans le constructeur de la classe Application() (juste
avant linstruction self.pack(), par exemple) :
mBar.mo.invoke(2)

Lorsque vous excutez le script ainsi modifi, vous constatez quau dpart la rubrique Musiciens de
la barre de menus est active, alors que la rubrique Peintres ne lest pas. Programmes comme elles
le sont, ces deux rubriques devraient tre actives toutes deux par dfaut. Et cest effectivement ce qui
se passe si nous supprimons linstruction mBar.mo.invoke(2).
Nous vous avons suggr dajouter cette instruction au script pour vous montrer comment vous
pouvez effectuer par programme la mme opration que celle que lon obtient normalement avec un
clic de souris.
Linstruction ci-dessus invoque le widget mBar.mo en actionnant la commande associe au deuxime
item de ce widget. En consultant le listing, vous pouvez vrifier que ce deuxime item est bien lobjet
de type checkbutton qui active/dsactive le menu Peintres (rappelons encore une fois que lon
numrote toujours partir de zro).

Fentres avec menus

253

Au dmarrage du programme, tout se passe donc comme si lutilisateur effectuait tout de suite un
premier clic sur la rubrique Peintres du menu Options , ce qui a pour effet de dsactiver le menu
correspondant.
Exercice

14.1 Perfectionnez le widget combo box simplifi dcrit la page 225, de manire ce que la
liste soit cache au dpart, et quun petit bouton droite du champ dentre le fasse
apparatre. Vous devrez pour ce faire placer la liste et son ascenseur dans une fentre satellite
sans bordure (cf. widget Toplevel, page 235), positionner celle-ci correctement (il vous faudra
probablement consulter les sites web traitant de tkinter pour trouver les informations
ncessaires, mais cela fait partie de votre apprentissage !), et vous assurer que cette fentre
disparaisse aprs que lutilisateur a slectionn un item dans la liste.

15
Analyse de programmes concrets

15

Dans ce chapitre, nous allons nous efforcer dillustrer la dmarche de conception dun programme
graphique, depuis ses premires bauches jusqu un stade de dveloppement relativement avanc.
Nous souhaitons montrer ainsi combien la programmation oriente objet peut faciliter et surtout
scuriser la stratgie de dveloppement incrmental que nous prconisons89.
Lutilisation de classes simpose, lorsque lon constate quun projet en cours de ralisation se rvle
nettement plus complexe que ce que lon avait imagin au dpart. Vous vivrez certainement
vous-mme des cheminements similaires celui que nous dcrivons ci-dessous.

Jeu des bombardes


Ce projet de jeu90 sinspire dun travail similaire ralis par des lves de Terminale.
Il est vivement recommand de commencer lbauche dun tel projet par une srie de petits dessins et
de schmas, dans lesquels seront dcrits les diffrents lments graphiques construire, ainsi quun
maximum de cas dutilisations. Si vous rechignez utiliser pour cela la bonne vieille technologie
papier/crayon (laquelle a pourtant bien fait ses preuves), vous pouvez tirer profit dun logiciel de
dessin technique, tel lutilitaire Draw de la suite bureautique OpenOffice.org91 qui nous a servi pour
raliser le schma de la page suivante.
Lide de dpart est simple : deux joueurs saffrontent au canon. Chacun doit ajuster son angle de tir
pour tcher datteindre son adversaire, les obus dcrivant des trajectoires balistiques.
Lemplacement des canons est dfini au dbut du jeu de manire alatoire (tout au moins en hauteur).
Aprs chaque tir, les canons se dplacent (afin daccrotre lintrt du jeu, lajustement des tirs tant
ainsi rendu plus difficile). Les coups au but sont comptabiliss.
89 Voir page 8 : recherche des erreurs et exprimentation, et aussi page 242 : fentres avec menus.
90 Nous nhsitons pas discuter ici le dveloppement d un logiciel de jeu, parce qu il sagit

dun domaine
directement accessible tous, et dans lequel les objectifs concrets sont aisment identifiables. Il va de soi que
les mmes techniques de dveloppement peuvent sappliquer dautres applications plus srieuses .
91 Il sagit dune suite bureautique complte, libre et gratuite, largement compatible avec MS-Office,
disponible pour Linux, Windows, Mac OS, Solaris... Le prsent manuel a t entirement rdig avec son
traitement de texte. Vous pouvez vous la procurer par tlchargement depuis le site web :
http://www.openoffice.org

256

Analyse de programmes concrets

Le dessin prliminaire que nous avons reproduit ci-dessus est lune des formes que peut prendre votre
travail danalyse. Avant de commencer le dveloppement dun projet de programmation, il vous faut en
effet toujours vous efforcer dtablir un cahier des charges dtaill. Cette tude pralable est trs
importante. La plupart des dbutants commencent bien trop vite crire de nombreuses lignes de
code au dpart dune vague ide, en ngligeant de rechercher la structure densemble. Leur
programmation risque alors de devenir chaotique, parce quils devront de toute faon mettre en place
cette structure tt ou tard. Il sapercevront alors bien souvent quil leur faut supprimer et r-crire
des pans entiers dun projet quils ont conu dune manire trop monolithique et/ou mal paramtre.

Trop monolithique : cela signifie que lon a nglig de dcomposer un problme complexe en
plusieurs sous-problmes plus simples. Par exemple, on a imbriqu plusieurs niveaux successifs
dinstructions composes, au lieu de faire appel des fonctions ou des classes.
Mal paramtre : cela signifie que lon a trait seulement un cas particulier, au lieu denvisager le
cas gnral. Par exemple, on a donn un objet graphique des dimensions fixes, au lieu de prvoir
des variables pour permettre son redimensionnement.
Vous devez donc toujours commencer le dveloppement dun projet par une phase danalyse aussi
fouille que possible, et concrtiser le rsultat de cette analyse dans un ensemble de documents
(schmas, plans, descriptions...) qui constitueront le cahier des charges. Pour les projets de grande
envergure, il existe dailleurs des mthodes danalyse trs labores (UML, Merise...) que nous ne
pouvons nous permettre de dcrire ici car elles font lobjet de livres entiers.
Cela tant dit, il faut malheureusement admettre quil est trs difficile (et mme probablement
impossible) de raliser ds le dpart lanalyse tout fait complte dun projet de programmation. Cest
seulement lorsquil commence fonctionner vritablement quun programme rvle ses faiblesses. On
constate alors quil reste des cas dutilisation ou des contraintes qui navaient pas t prvues au
dpart. Dautre part, un projet logiciel est pratiquement toujours destin voluer : il vous arrivera
frquemment de devoir modifier le cahier des charges au cours du dveloppement lui-mme, pas
ncessairement parce que lanalyse initiale a t mal faite, mais tout simplement parce que lon
souhaite ajouter des fonctionnalits supplmentaires.
En conclusion, tchez de toujours aborder un nouveau projet de programmation en respectant les
deux consignes suivantes :

Dcrivez votre projet en profondeur avant de commencer la rdaction des premires lignes de
code, en vous efforant de mettre en vidence les composants principaux et les relations qui les
lient (pensez notamment dcrire les diffrents cas dutilisation de votre programme).
Lorsque vous commencerez sa ralisation effective, vitez de vous laisser entraner rdiger de
trop grands blocs dinstructions. Veillez au contraire dcouper votre application en un certain
nombre de composants paramtrables bien encapsuls, de telle manire que vous puissiez aisment
modifier lun ou lautre dentre eux sans compromettre le fonctionnement des autres, et peut-tre
mme les rutiliser dans diffrents contextes si le besoin sen fait sentir.
Cest pour satisfaire cette exigence que la programmation oriente objets est a t invente.
Considrons par exemple lbauche dessine la page prcdente.

Jeu des bombardes

257

Lapprenti programmeur sera peut-tre tent de commencer la ralisation de ce jeu en nutilisant que
la seule programmation procdurale (cest--dire en omettant de dfinir de nouvelles classes). Cest
dailleurs ainsi que nous avons procd nous-mme lors de notre premire approche des interfaces
graphiques, tout au long du chapitre 8. Cette faon de procder ne se justifie cependant que pour de
tout petits programmes (des exercices ou des tests prliminaires). Lorsque lon sattaque un projet
dune certaine importance, la complexit des problmes qui se prsentent se rvle rapidement trop
importante, et il devient alors indispensable de fragmenter et de compartimenter.
Loutil logiciel qui va permettre cette fragmentation est la classe.
Nous pouvons peut-tre mieux comprendre son utilit en nous aidant dune analogie.
Tous les appareils lectroniques sont constitus dun petit nombre de composants de base, savoir des
transistors, des diodes, des rsistances, des condensateurs, etc. Les premiers ordinateurs ont t
construits directement partir de ces composants. Ils taient volumineux, trs chers, et pourtant ils
navaient que trs peu de fonctionnalits et tombaient frquemment en panne.
On a alors dvelopp diffrentes techniques pour encapsuler dans un mme botier un certain nombre
de composants lectroniques de base. Pour utiliser ces nouveaux circuits intgrs, il ntait plus
ncessaire de connatre leur contenu exact : seule importait leur fonction globale. Les premires
fonctions intgres taient encore relativement simples : ctaient par exemple des portes logiques,
des bascules, etc. En combinant ces circuits entre eux, on obtenait des caractristiques plus labores,
telles que des registres ou des dcodeurs, qui purent leur tour tre intgrs, et ainsi de suite,
jusquaux microprocesseurs actuels. Ceux-ci contiennent dornavant plusieurs millions de
composants, et pourtant leur fiabilit reste extrmement leve.
En consquence, pour llectronicien moderne qui veut construire par exemple un compteur binaire
(circuit qui ncessite un certain nombre de bascules), il est videmment bien plus simple, plus rapide
et plus sr de se servir de bascules intgres, plutt que de schiner combiner sans erreur plusieurs
centaines de transistors et de rsistances.
Dune manire analogue, le programmeur moderne que vous tes peut bnficier du travail accumul
par ses prdcesseurs en utilisant la fonctionnalit intgre dans les nombreuses bibliothques de
classes dj disponibles pour Python. Mieux encore, il peut aisment crer lui-mme de nouvelles
classes pour encapsuler les principaux composants de son application, particulirement ceux qui y
apparaissent en plusieurs exemplaires. Procder ainsi est plus simple, plus rapide et plus sr que de
multiplier les blocs dinstructions similaires dans un corps de programme monolithique, de plus en
plus volumineux et de moins en moins comprhensible.
Examinons prsent notre bauche dessine. Les composants les plus importants de ce jeu sont bien
videmment les petits canons, quil faudra pouvoir dessiner diffrents emplacements et dans
diffrentes orientations, et dont il nous faudra au moins deux exemplaires.
Plutt que de les dessiner morceau par morceau dans le canevas au fur et mesure du droulement du
jeu, nous avons intrt les considrer comme des objets logiciels part entire, dots de plusieurs
proprits ainsi que dun certain comportement (ce que nous voulons exprimer par l est le fait quil
devront tre dots de divers mcanismes, que nous pourrons activer par programme laide de mthodes particulires). Il est donc certainement judicieux de leur consacrer une classe spcifique.

258

Analyse de programmes concrets

Prototypage dune classe Canon


En dfinissant une telle classe, nous gagnons sur plusieurs tableaux. Non seulement nous rassemblons
ainsi tout le code correspondant au dessin et au fonctionnement du canon dans une mme capsule ,
bien lcart du reste du programme, mais de surcrot nous nous donnons la possibilit dinstancier
aisment un nombre quelconque de ces canons dans le jeu, ce qui nous ouvre des perspectives de
dveloppements ultrieurs.
Lorsquune premire implmentation de la classe Canon() aura t construite et teste, il sera
galement possible de la perfectionner en la dotant de caractristiques supplmentaires, sans modifier
(ou trs peu) son interface, cest--dire en quelque sorte son mode demploi : savoir les
instructions ncessaires pour linstancier et lutiliser dans des applications diverses.
Entrons prsent dans le vif du sujet.
Le dessin de notre canon peut tre simplifi lextrme. Nous
avons estim quil pouvait se rsumer un cercle combin avec un
rectangle, celui-ci pouvant dailleurs tre lui-mme considr
comme un simple segment de ligne droite particulirement pais.
Si lensemble est rempli dune couleur uniforme (en noir, par
exemple), nous obtiendrons ainsi une sorte de petite bombarde
suffisamment crdible.
Dans la suite du raisonnement, nous admettrons que la position du canon est en fait la position du
centre du cercle (coordonnes x et y dans le dessin ci-contre). Ce point cl indique galement laxe de
rotation de la buse du canon, ainsi que lune des extrmits de la ligne paisse qui reprsentera cette
buse.
Pour terminer notre dessin, il nous restera alors dterminer les coordonnes de lautre extrmit de
cette ligne. Ces coordonnes peuvent tre calcules sans grande difficult, la condition de nous
remmorer deux concepts fondamentaux de la trigonomtrie (le sinus et le cosinus) que vous devez
certainement bien connatre :
Dans un triangle rectangle, le rapport entre le cot oppos un angle et
lhypotnuse du triangle est une proprit spcifique de cet angle quon
appelle sinus de langle. Le cosinus du mme angle est le rapport entre le
ct adjacent langle et lhypotnuse.
Ainsi, dans le schma ci-contre : sin =

a
b
et cos = .
h
h

Pour reprsenter la buse de notre canon, en supposant que nous connaissions sa longueur l et langle
de tir , il nous faut donc tracer un segment de ligne droite paisse, partir des coordonnes du
centre du cercle (x et y), jusqu un autre point situ plus droite et plus haut, lcart horizontal x
tant gal l.cos , et lcart vertical y tant gal l.sin .
En rsumant tout ce qui prcde, dessiner un canon au point x, y consistera simplement :

tracer un cercle noir centr sur x, y ;

Jeu des bombardes

259

tracer une ligne noire paisse depuis le point x, y jusquau point x + l.cos , y + l.sin .
Nous pouvons prsent commencer envisager une bauche de programmation correspondant une
classe Canon . Il nest pas encore question ici de programmer le jeu proprement dit. Nous voulons
seulement vrifier si lanalyse que nous avons faite jusqu prsent tient la route , en ralisant un
premier prototype fonctionnel.
Un prototype est un petit programme destin exprimenter une ide, que lon se propose dintgrer
ensuite dans une application plus vaste. Du fait de sa simplicit et de sa concision, Python se prte fort
bien llaboration de prototypes, et de nombreux programmeurs lutilisent pour mettre au point
divers composants logiciels quils reprogrammeront ventuellement ensuite dans dautres langages
plus lourds , tels que le C par exemple.
Dans notre premier prototype, la classe Canon() ne comporte que deux mthodes : un constructeur
qui cre les lments de base du dessin, et une mthode permettant de modifier celui-ci volont pour
ajuster langle de tir (linclinaison de la buse). Comme nous lavons souvent fait dans dautres
exemples, nous inclurons quelques lignes de code la fin du script afin de pouvoir tester la classe tout
de suite :
1#
2#
3#
4#
5#
6#
7#
8#
9#
10#
11#
12#
13#
14#
15#
16#
17#
18#
19#
20#
21#
22#
23#
24#
25#
26#
27#
28#
29#
30#
31#
32#
33#
34#
35#
36#
37#
38#

from tkinter import *


from math import pi, sin, cos
class Canon(object):
"""Petit canon graphique"""
def __init__(self, boss, x, y):
self.boss = boss
# rfrence du canevas
self.x1, self.y1 = x, y
# axe de rotation du canon
# dessiner la buse du canon, l'horizontale pour commencer :
self.lbu = 50
# longueur de la buse
self.x2, self.y2 = x + self.lbu, y
self.buse = boss.create_line(self.x1, self.y1, self.x2, self.y2,
width =10)
# dessiner ensuite le corps du canon par-dessus :
r = 15
# rayon du cercle
boss.create_oval(x-r, y-r, x+r, y+r, fill='blue', width =3)
def orienter(self, angle):
"choisir l'angle de tir du canon"
# rem : le paramtre <angle> est reu en tant que chane de car.
# il faut le traduire en nombre rel, puis convertir en radians :
self.angle = float(angle)*2*pi/360
self.x2 = self.x1 + self.lbu*cos(self.angle)
self.y2 = self.y1 - self.lbu*sin(self.angle)
self.boss.coords(self.buse, self.x1, self.y1, self.x2, self.y2)
if __name__ == '__main__':
# Code pour tester sommairement la classe Canon :
f = Tk()
can = Canvas(f,width =250, height =250, bg ='ivory')
can.pack(padx =10, pady =10)
c1 = Canon(can, 50, 200)
s1 =Scale(f, label='hausse', from_=90, to=0, command=c1.orienter)
s1.pack(side=LEFT, pady =5, padx =20)
s1.set(25)
# angle de tir initial
f.mainloop()

260

Analyse de programmes concrets

Commentaires

Ligne 6 : Dans la liste des paramtres qui devront tre transmis au constructeur lors de linstanciation, nous prvoyons les coordonnes x et y, qui indiqueront lemplacement du canon dans le
canevas, mais galement une rfrence au canevas lui-mme (la variable boss). Cette rfrence est
indispensable : elle sera utilise pour invoquer les mthodes du canevas.
Nous pourrions inclure aussi un paramtre pour choisir un angle de tir initial, mais puisque nous
avons lintention dimplmenter une mthode spcifique pour rgler cette orientation, il sera plus
judicieux de faire appel celle-ci au moment voulu.
Lignes 7-8 : Ces rfrences seront utilises un peu partout dans les diffrentes mthodes que nous
allons dvelopper dans la classe. Il faut donc en faire des attributs dinstance.
Lignes 9 16 : Nous dessinons la buse dabord, et le corps du canon ensuite. Ainsi une partie de la
buse reste cache. Cela nous permet de colorer ventuellement le corps du canon.
Lignes 18 25 : Cette mthode sera invoque avec un argument angle, lequel sera fourni en degrs
(compts partir de lhorizontale). Sil est produit laide dun widget tel que Entry ou Scale, il
sera transmis sous la forme dune chane de caractres, et nous devrons donc le convertir dabord
en nombre rel avant de lutiliser dans nos calculs (ceux-ci ont t dcrits la page prcdente).
Lignes 27 38 : Pour tester notre nouvelle classe, nous ferons usage dun widget Scale. Pour
dfinir la position initiale de son curseur, et donc fixer langle de hausse initial du canon, nous
devons faire appel sa mthode set() (ligne 36).

Jeu des bombardes

261

Ajout de mthodes au prototype


Notre prototype est fonctionnel, mais beaucoup trop
rudimentaire. Nous devons prsent le perfectionner pour
lui ajouter la capacit de tirer des obus.
Ceux-ci seront traits plutt comme des boulets : ce
seront de simples petits cercles que nous ferons partir de la
bouche du canon avec une vitesse initiale dorientation
identique celle de sa buse. Pour leur faire suivre une
trajectoire raliste, nous devons prsent nous rappeler
quelques lments de physique.
Comment un objet lanc et laiss lui-mme volue-t-il dans
lespace, si lon nglige les phnomnes secondaires tels que la
rsistance de lair ?
Ce problme peut vous paratre complexe, mais en ralit sa
rsolution est trs simple : il vous suffit dadmettre que le
boulet se dplace la fois horizontalement et verticalement,
et que ces deux mouvements, quoique simultans, sont tout
fait indpendants lun de lautre.
Vous allez donc tablir une boucle danimation, dans laquelle vous recalculez les nouvelles
coordonnes x et y du boulet intervalles de temps rguliers, en sachant que :

Le mouvement horizontal est uniforme. chaque itration, il vous suffit daugmenter


graduellement la coordonne x du boulet, en lui ajoutant toujours un mme dplacement x.
Le mouvement vertical est uniformment acclr. Cela signifie simplement qu chaque itration,
vous devez ajouter la coordonne y un dplacement y qui augmente lui-mme graduellement,
toujours de la mme quantit.
Voyons cela dans le script :
Pour commencer, il faut ajouter les lignes suivantes la fin de la mthode constructeur. Elles vont
servir crer lobjet obus , et prparer une variable dinstance qui servira dinterrupteur de lanimation. Lobus est cr au dpart avec des dimensions minimales (un cercle dun seul pixel) afin de
rester presque invisible :
# dessiner un obus (rduit un simple point, avant animation) :
self.obus =boss.create_oval(x, y, x, y, fill=red)
self.anim =False
# interrupteur danimation
# retrouver la largeur et la hauteur du canevas :
self.xMax =int(boss.cget(width))
self.yMax =int(boss.cget(height))

Les deux dernires lignes utilisent la mthode cget() du widget matre (le canevas, ici), afin de
retrouver certaines de ses caractristiques. Nous voulons en effet que notre classe Canon soit
gnraliste, cest--dire rutilisable dans nimporte quel contexte, et nous ne pouvons donc pas tabler
lavance sur des dimensions particulires pour le canevas dans lequel ce canon sera utilis.

262

Analyse de programmes concrets


tkinter renvoie ces valeurs sous la forme de chanes de caractres. Il faut donc les
convertir dans un type numrique si nous voulons pouvoir les utiliser dans un calcul.

Ensuite, nous devons ajouter deux nouvelles mthodes : lune pour dclencher le tir, et lautre pour
grer lanimation du boulet une fois que celui-ci aura t lanc :
1#
2#
3#
4#
5#
6#
7#
8#
9#
10#
11#
12#
13#
14#
15#
16#
17#
18#
19#
20#
21#
22#
23#

def feu(self):
"dclencher le tir d'un obus"
if not self.anim:
self.anim =True
# position de dpart de l'obus (c'est la bouche
self.boss.coords(self.obus, self.x2 -3, self.y2
self.x2 +3, self.y2
v =15
# vitesse initiale
# composantes verticale et horizontale de cette
self.vy = -v *sin(self.angle)
self.vx = v *cos(self.angle)
self.animer_obus()

du canon) :
-3,
+3)
vitesse :

def animer_obus(self):
"animation de l'obus (trajectoire balistique)"
if self.anim:
self.boss.move(self.obus, int(self.vx), int(self.vy))
c = tuple(self.boss.coords(self.obus))
# coord. rsultantes
xo, yo = c[0] +3, c[1] +3
# coord. du centre de l'obus
if yo > self.yMax or xo > self.xMax:
self.anim =False
# arrter l'animation
self.vy += .5
self.boss.after(30, self.animer_obus)

Commentaires

Lignes 1 4 : Cette mthode sera invoque par appui sur un bouton. Elle dclenche le mouvement
de lobus, et attribue une valeur vraie notre interrupteur danimation (la variable
self.anim : voir ci-aprs). Il faut cependant nous assurer que pendant toute la dure de cette
animation, un nouvel appui sur le bouton ne puisse pas activer dautres boucles danimation
parasites. Cest le rle du test effectu la ligne 3 : le bloc dinstruction qui suit ne peut sexcuter
que si la variable self.anim possde la valeur faux , ce qui signifie que lanimation na pas
encore commenc.
Lignes 5 7 : Le canevas tkinter dispose de deux mthodes pour dplacer les objets graphiques :
La mthode coords() (utilise la ligne 6) effectue un positionnement absolu ; il faut cependant
lui fournir toutes les coordonnes de lobjet (comme si on le redessinait).
La mthode move() (utilise plus loin, la ligne 17), provoque un dplacement relatif ; elle sutilise avec deux arguments seulement, savoir les composantes horizontale et verticale du
dplacement souhait.
Lignes 8 12 : La vitesse initiale de lobus est choisie la ligne 8. Comme nous lavons expliqu la
page prcdente, le mouvement du boulet est la rsultante dun mouvement horizontal et dun
mouvement vertical. Nous connaissons la valeur de la vitesse initiale ainsi que son inclinaison
(cest--dire langle de tir). Pour dterminer les composantes horizontale et verticale de cette
vitesse, il nous suffit dutiliser des relations trigonomtriques tout fait similaires celles que
nous avons dj exploites pour dessiner la buse du canon. Le signe - utilis la ligne 10 provient

Jeu des bombardes

263

du fait que les coordonnes verticales se comptent de haut en bas.


La ligne 12 active lanimation proprement dite.
Lignes 14 23 : Cette procdure se r-appelle elle-mme toutes les 30 millisecondes par lintermdiaire de la mthode after() invoque la ligne 23. Cela continue aussi longtemps que la variable
self.anim (notre interrupteur danimation ) reste vraie , condition qui changera lorsque les
coordonnes de lobus sortiront des limites imposes (test de la ligne 20).
Lignes 18-19 : Pour retrouver ces coordonnes aprs chaque dplacement, on fait appel encore
une fois la mthode coords() du canevas : utilise cette fois avec la rfrence dun objet
graphique comme unique argument, elle renvoie ses quatre coordonnes dans un objet itrable
que lon peut convertir en une liste ou un tuple laide des fonctions intgres list() et tuple().
Lignes 17-22 : La coordonne horizontale de lobus augmente toujours de la mme quantit
(mouvement uniforme), tandis que la coordonne verticale augmente dune quantit qui est
elle-mme augmente chaque fois la ligne 24 (mouvement uniformment acclr). Le rsultat
est une trajectoire parabolique.
Loprateur += permet dincrmenter une variable :
ainsi a += 3 quivaut a = a + 3. Veuillez noter au passage que lutilisation de cet
oprateur spcifique est plus efficace que la raffectation utilise jusquici.
partir de la version 2.3, Python initialise automatiquement deux variables nommes
True et False pour reprsenter la vracit et la fausset dune expression (notez bien
que ces noms commencent tous deux par une majuscule). Comme nous lavons fait dans
le script ci-dessus, vous pouvez utiliser ces variables dans les expressions
conditionnelles afin daugmenter la lisibilit de votre code. Si vous prfrez, vous pouvez
cependant continuer utiliser aussi des valeurs numriques, comme nous l avons fait
prcdemment. (cf. Vracit/Fausset dune expression , page 53).

Il reste enfin ajouter un bouton dclencheur dans la fentre principale. Une ligne telle que la
suivante ( insrer dans le code de test) fera parfaitement laffaire :
Button(f, text='Feu !', command =c1.feu).pack(side=LEFT)

Dveloppement de lapplication
Disposant dsormais dune classe dobjets canon assez bien dgrossie, nous pouvons envisager
llaboration de lapplication proprement dite. Et puisque nous sommes dcids exploiter la
mthodologie de la programmation oriente objet, nous devons concevoir cette application comme un
ensemble dobjets qui interagissent par lintermdiaire de leurs mthodes.
Plusieurs de ces objets proviendront de classes prexistantes, bien entendu : ainsi le canevas, les
boutons, etc. Mais nous avons vu dans les pages prcdentes que nous avons intrt regrouper des
ensembles bien dlimits de ces objets basiques dans de nouvelles classes, chaque fois que nous
pouvons identifier pour ces ensembles une fonctionnalit particulire. Ctait le cas par exemple pour
cet ensemble de cercles et de lignes mobiles que nous avons dcid dappeler canon .

264

Analyse de programmes concrets

Pouvons-nous encore distinguer dans notre projet initial dautres composants qui mriteraient dtre
encapsuls dans des nouvelles classes ? Certainement. Il y a par exemple le pupitre de contrle que
nous voulons associer chaque canon : nous pouvons y rassembler le dispositif de rglage de la hausse
(langle de tir), le bouton de mise feu, le score ralis, et peut-tre dautres indications encore,
comme le nom du joueur. Il est dautant plus intressant de lui consacrer une classe particulire, que
nous savons demble quil nous en faudra deux instances.
Il y a aussi lapplication elle-mme, bien sr. En lencapsulant dans une classe, nous en ferons notre
objet principal, celui qui dirigera tous les autres.
Veuillez prsent analyser le script ci-dessous. Vous y retrouverez la classe Canon() encore davantage
dveloppe : nous y avons ajout quelques attributs et trois mthodes supplmentaires, afin de
pouvoir grer les dplacements du canon lui-mme, ainsi que les coups au but.
La classe Application() remplace dsormais le code de test des prototypes prcdents. Nous y
instancions deux objets Canon(), et deux objets de la nouvelle classe Pupitre(), que nous plaons dans
des dictionnaires en prvision de dveloppements ultrieurs (nous pouvons en effet imaginer d augmenter le nombre de canons et donc de pupitres). Le jeu est prsent fonctionnel : les canons se
dplacent aprs chaque tir, et les coups au but sont comptabiliss.
1#
2#
3#
4#
5#
6#
7#
8#
9#

from tkinter import *


from math import sin, cos, pi
from random import randrange
class Canon(object):
"""Petit canon graphique"""
def __init__(self, boss, id, x, y, sens, coul):
self.boss = boss
# rf. du canevas
self.appli = boss.master
# rf. de la fentre d'application

Jeu des bombardes


10#
11#
12#
13#
14#
15#
16#
17#
18#
19#
20#
21#
22#
23#
24#
25#
26#
27#
28#
29#
30#
31#
32#
33#
34#
35#
36#
37#
38#
39#
40#
41#
42#
43#
44#
45#
46#
47#
48#
49#
50#
51#
52#
53#
54#
55#
56#
57#
58#
59#
60#
61#
62#
63#
64#
65#
66#
67#
68#
69#
70#
71#
72#
73#

self.id = id
# identifiant du canon (chane)
self.coul = coul
# couleur associe au canon
self.x1, self.y1 = x, y
# axe de rotation du canon
self.sens = sens
# sens de tir (-1:gauche, +1:droite)
self.lbu = 30
# longueur de la buse
self.angle = 0
# hausse par dfaut (angle de tir)
# retrouver la largeur et la hauteur du canevas :
self.xMax = int(boss.cget('width'))
self.yMax = int(boss.cget('height'))
# dessiner la buse du canon (horizontale) :
self.x2, self.y2 = x + self.lbu * sens, y
self.buse = boss.create_line(self.x1, self.y1,
self.x2, self.y2, width =10)
# dessiner le corps du canon (cercle de couleur) :
self.rc = 15
# rayon du cercle
self.corps = boss.create_oval(x -self.rc, y -self.rc, x +self.rc,
y +self.rc, fill =coul)
# pr-dessiner un obus cach (point en dehors du canevas) :
self.obus = boss.create_oval(-10, -10, -10, -10, fill='red')
self.anim = False
# indicateurs d'animation
self.explo = False
#
et d'explosion
def orienter(self, angle):
"rgler la hausse du canon"
# rem: le paramtre <angle> est reu en tant que chane.
# Il faut donc le traduire en rel, puis le convertir en radians :
self.angle = float(angle)*pi/180
# rem: utiliser la mthode coords de prfrence avec des entiers :
self.x2 = int(self.x1 + self.lbu * cos(self.angle) * self.sens)
self.y2 = int(self.y1 - self.lbu * sin(self.angle))
self.boss.coords(self.buse, self.x1, self.y1, self.x2, self.y2)
def deplacer(self, x, y):
"amener le canon dans une nouvelle position x, y"
dx, dy = x -self.x1, y -self.y1
# valeur du dplacement
self.boss.move(self.buse, dx, dy)
self.boss.move(self.corps, dx, dy)
self.x1 += dx
self.y1 += dy
self.x2 += dx
self.y2 += dy
def feu(self):
"tir d'un obus - seulement si le prcdent a fini son vol"
if not (self.anim or self.explo):
self.anim =True
# rcuprer la description de tous les canons prsents :
self.guns = self.appli.dictionnaireCanons()
# position de dpart de l'obus (c'est la bouche du canon) :
self.boss.coords(self.obus, self.x2 -3, self.y2 -3,
self.x2 +3, self.y2 +3)
v = 17
# vitesse initiale
# composantes verticale et horizontale de cette vitesse :
self.vy = -v *sin(self.angle)
self.vx = v *cos(self.angle) *self.sens
self.animer_obus()
return True
# => signaler que le coup est parti
else:
return False
# => le coup n'a pas pu tre tir
def animer_obus(self):
"animer l'obus (trajectoire balistique)"
if self.anim:
self.boss.move(self.obus, int(self.vx), int(self.vy))

265

266

Analyse de programmes concrets

74#
c = tuple(self.boss.coords(self.obus))
# coord. rsultantes
75#
xo, yo = c[0] +3, c[1] +3
# coord. du centre de l'obus
76#
self.test_obstacle(xo, yo)
# a-t-on atteint un obstacle ?
77#
self.vy += .4
# acclration verticale
78#
self.boss.after(20, self.animer_obus)
79#
else:
80#
# animation termine - cacher l'obus et dplacer les canons :
81#
self.fin_animation()
82#
83#
def test_obstacle(self, xo, yo):
84#
"valuer si l'obus a atteint une cible ou les limites du jeu"
85#
if yo >self.yMax or xo <0 or xo >self.xMax:
86#
self.anim =False
87#
return
88#
# analyser le dictionnaire des canons pour voir si les coord.
89#
# de l'un d'entre eux sont proches de celles de l'obus :
90#
for id in self.guns:
# id = clef dans dictionn.
91#
gun = self.guns[id]
# valeur correspondante
92#
if xo < gun.x1 +self.rc and xo > gun.x1 -self.rc \
93#
and yo < gun.y1 +self.rc and yo > gun.y1 -self.rc :
94#
self.anim =False
95#
# dessiner l'explosion de l'obus (cercle jaune) :
96#
self.explo = self.boss.create_oval(xo -12, yo -12,
97#
xo +12, yo +12, fill ='yellow', width =0)
98#
self.hit =id
# rfrence de la cible touche
99#
self.boss.after(150, self.fin_explosion)
100#
break
101#
102#
def fin_explosion(self):
103#
"effacer l'explosion ; rinitaliser l'obus ; grer le score"
104#
self.boss.delete(self.explo)
# effacer l'explosion
105#
self.explo =False
# autoriser un nouveau tir
106#
# signaler le succs la fentre matresse :
107#
self.appli.goal(self.id, self.hit)
108#
109#
def fin_animation(self):
110#
"actions accomplir lorsque l'obus a termin sa trajectoire"
111#
self.appli.disperser()
# dplacer les canons
112#
# cacher l'obus (en l'expdiant hors du canevas) :
113#
self.boss.coords(self.obus, -10, -10, -10, -10)
114#
115# class Pupitre(Frame):
116#
"""Pupitre de pointage associ un canon"""
117#
def __init__(self, boss, canon):
118#
Frame.__init__(self, bd =3, relief =GROOVE)
119#
self.score =0
120#
self.appli =boss
# rf. de l'application
121#
self.canon =canon
# rf. du canon associ
122#
# Systme de rglage de l'angle de tir :
123#
self.regl =Scale(self, from_ =85, to =-15, troughcolor=canon.coul,
124#
command =self.orienter)
125#
self.regl.set(45)
# angle initial de tir
126#
self.regl.pack(side =LEFT)
127#
# tiquette d'identification du canon :
128#
Label(self, text =canon.id).pack(side =TOP, anchor =W, pady =5)
129#
# Bouton de tir :
130#
self.bTir =Button(self, text ='Feu !', command =self.tirer)
131#
self.bTir.pack(side =BOTTOM, padx =5, pady =5)
132#
Label(self, text ="points").pack()
133#
self.points =Label(self, text=' 0 ', bg ='white')
134#
self.points.pack()
135#
# positionner gauche ou droite suivant le sens du canon :
136#
if canon.sens == -1:
137#
self.pack(padx =5, pady =5, side =RIGHT)

Jeu des bombardes


138#
else:
139#
self.pack(padx =5, pady =5, side =LEFT)
140#
141#
def tirer(self):
142#
"dclencher le tir du canon associ"
143#
self.canon.feu()
144#
145#
def orienter(self, angle):
146#
"ajuster la hausse du canon associ"
147#
self.canon.orienter(angle)
148#
149#
def attribuerPoint(self, p):
150#
"incrmenter ou dcrmenter le score, de <p> points"
151#
self.score += p
152#
self.points.config(text = ' %s ' % self.score)
153#
154# class Application(Frame):
155#
'''Fentre principale de l'application'''
156#
def __init__(self):
157#
Frame.__init__(self)
158#
self.master.title('>>>>> Boum ! Boum ! <<<<<')
159#
self.pack()
160#
self.jeu = Canvas(self, width =400, height =250, bg ='ivory',
161#
bd =3, relief =SUNKEN)
162#
self.jeu.pack(padx =8, pady =8, side =TOP)
163#
164#
self.guns ={}
# dictionnaire des canons prsents
165#
self.pupi ={}
# dictionnaire des pupitres prsents
166#
# Instanciation de 2 objets canons (+1, -1 = sens opposs) :
167#
self.guns["Billy"] = Canon(self.jeu, "Billy", 30, 200, 1, "red")
168#
self.guns["Linus"] = Canon(self.jeu, "Linus", 370,200,-1, "blue")
169#
# Instanciation de 2 pupitres de pointage associs ces canons :
170#
self.pupi["Billy"] = Pupitre(self, self.guns["Billy"])
171#
self.pupi["Linus"] = Pupitre(self, self.guns["Linus"])
172#
173#
def disperser(self):
174#
"dplacer alatoirement les canons"
175#
for id in self.guns:
176#
gun =self.guns[id]
177#
# positionner gauche ou droite, suivant sens du canon :
178#
if gun.sens == -1 :
179#
x = randrange(320,380)
180#
else:
181#
x = randrange(20,80)
182#
# dplacement proprement dit :
183#
gun.deplacer(x, randrange(150,240))
184#
185#
def goal(self, i, j):
186#
"le canon <i> signale qu'il a atteint l'adversaire <j>"
187#
if i != j:
188#
self.pupi[i].attribuerPoint(1)
189#
else:
190#
self.pupi[i].attribuerPoint(-1)
191#
192#
def dictionnaireCanons(self):
193#
"renvoyer le dictionnaire dcrivant les canons prsents"
194#
return self.guns
195#
196# if __name__ =='__main__':
197#
Application().mainloop()

267

268

Analyse de programmes concrets

Commentaires

Ligne 7 : Par rapport au prototype, trois paramtres ont t ajouts la mthode constructeur. Le
paramtre id nous permet didentifier chaque instance de la classe Canon() laide dun nom
quelconque. Le paramtre sens indique sil sagit dun canon qui tire vers la droite (sens = 1) ou
vers la gauche (sens = -1). Le paramtre coul spcifie la couleur associe au canon.
Ligne 9 : Tous les widgets tkinter possdent un attribut master qui contient la rfrence leur
widget matre ventuel (leur contenant ). Cette rfrence est donc pour nous celle de lapplication principale. Nous avons implment nous-mmes une technique similaire pour rfrencer le
canevas, laide de lattribut boss.
Lignes 42 50 : Cette mthode permet damener le canon dans un nouvel emplacement. Elle
servira repositionner les canons au hasard aprs chaque tir, ce qui augmente lintrt du jeu.
Lignes 56-57 : Nous essayons de construire notre classe canon de telle manire quelle puisse tre
rutilise dans des projets plus vastes, impliquant un nombre quelconque dobjets canons qui
pourront apparatre et disparatre au fil des combats. Dans cette perspective, il faut que nous
puissions disposer dune description de tous les canons prsents, avant chaque tir, de manire
pouvoir dterminer si une cible a t touche ou non. Cette description est gre par lapplication
principale, dans un dictionnaire, dont on peut demander une copie par lintermdiaire de sa
mthode dictionnaireCanons().
Lignes 66 68 : Dans cette mme perspective gnraliste, il peut tre utile dinformer
ventuellement le programme appelant que le coup a effectivement t tir ou non.
Ligne 76 : Lanimation de lobus est dsormais traite par deux mthodes complmentaires. Afin
de clarifier le code, nous avons plac dans une mthode distincte les instructions servant
dterminer si une cible a t atteinte (mthode test_obstacle()).
Lignes 79 81 : Nous avons vu prcdemment que lon interrompt lanimation de lobus en
attribuant une valeur fausse la variable self.anim. La mthode animer_obus() cesse alors de
boucler et excute le code de la ligne 81.
Lignes 83 100 : Cette mthode value si les coordonnes actuelles de lobus sortent des limites de
la fentre, ou encore si elles sapprochent de celles dun autre canon. Dans les deux cas, linterrupteur danimation est actionn, mais dans le second, on dessine une explosion jaune, et la
rfrence du canon touch est mmorise. La mthode annexe fin_explosion() est invoque aprs
un court laps de temps pour terminer le travail, cest--dire effacer le cercle dexplosion et
envoyer un message la fentre matresse pour signaler le coup au but.
Lignes 115 152 : La classe Pupitre() dfinit un nouveau widget par drivation de la classe
Frame(), selon une technique qui doit dsormais vous tre devenue familire. Ce nouveau widget
regroupe les commandes de hausse et de tir, ainsi que lafficheur de points associs un canon
bien dtermin. La correspondance visuelle entre les deux est assure par ladoption dune
couleur commune. Les mthodes tirer() et orienter() communiquent avec lobjet Canon() associ,
par lintermdiaire des mthodes de celui-ci.

Jeu des bombardes

269

Lignes 154 171 : La fentre dapplication est elle aussi un widget driv de Frame(). Son
constructeur instancie les deux canons et leurs pupitres de pointage, en plaant ces objets dans
les deux dictionnaires self.guns et self.pupi. Cela permet deffectuer ensuite divers traitements
systmatiques sur chacun deux (comme par exemple la mthode suivante). En procdant ainsi,
on se rserve en outre la possibilit daugmenter sans effort le nombre de ces canons si ncessaire,
dans les dveloppements ultrieurs du programme.
Lignes 173 183 : Cette mthode est invoque aprs chaque tir pour dplacer alatoirement les
deux canons, ce qui augmente la difficult du jeu.

Dveloppements complmentaires
Tel quil vient dtre dcrit, notre programme correspond dj plus ou moins au cahier des charges
initial, mais il est vident que nous pouvons continuer le perfectionner.
A) Nous devrions par exemple mieux le paramtrer. Quest-ce dire ? Dans sa forme actuelle, notre jeu
comporte un canevas de taille prdtermine (400 250 pixels, voir ligne 161). Si nous voulons
modifier ces valeurs, nous devons veiller modifier aussi les autres lignes du script o ces dimensions
interviennent (comme par exemple aux lignes 168-169, ou 179-184). De telles lignes interdpendantes
risquent de devenir nombreuses si nous ajoutons encore dautres fonctionnalits. Il serait donc plus
judicieux de dimensionner le canevas laide de variables, dont la valeur serait dfinie en un seul
endroit. Ces variables seraient ensuite exploites dans toutes les lignes dinstructions o les
dimensions du canevas interviennent.
Nous avons dj effectu une partie de ce travail : dans la classe Canon(), en effet, les dimensions du
canevas sont rcupres laide dune mthode prdfinie (voir lignes 17-18), et places dans des
attributs dinstance qui peuvent tre utiliss partout dans la classe.
B) Aprs chaque tir, nous provoquons un dplacement alatoire des canons, en redfinissant leurs
coordonnes au hasard. Il serait probablement plus raliste de provoquer de vritables dplacements
relatifs, plutt que de redfinir au hasard des positions absolues. Pour ce faire, il suffit de retravailler la
mthode deplacer() de la classe Canon(). En fait, il serait encore plus intressant de faire en sorte que
cette mthode puisse produire volont, aussi bien un dplacement relatif quun positionnement
absolu, en fonction dune valeur transmise en argument.
C) Le systme de commande des tirs devrait tre amlior : puisque nous ne disposons que dune seule
souris, il faut demander aux joueurs de tirer tour de rle, et nous navons mis en place aucun
mcanisme pour les forcer le faire. Une meilleure approche consisterait prvoir des commandes de
hausse et de tir utilisant certaines touches du clavier, qui soient distinctes pour les deux joueurs.
D) Mais le dveloppement le plus intressant pour notre programme serait certainement den faire
une application rseau. Le jeu serait alors install sur plusieurs machines communicantes, chaque joueur
ayant le contrle dun seul canon. Il serait dailleurs encore plus attrayant de permettre la mise en
uvre de plus de deux canons, de manire autoriser des combats impliquant davantage de joueurs.

270

Analyse de programmes concrets

Ce type de dveloppement suppose cependant que nous ayons appris matriser au pralable deux
domaines de programmation qui dbordent un peu le cadre de ce cours :

la technique des sockets, qui permet dtablir une communication entre deux ordinateurs ;
la technique des threads, qui permet un mme programme deffectuer plusieurs tches
simultanment (cela nous sera ncessaire, si nous voulons construire une application capable de
communiquer en mme temps avec plusieurs partenaires).
Ces matires ne font pas strictement partie des objectifs que nous nous sommes fixs pour ce cours, et
leur traitement ncessite lui seul un chapitre entier. Nous naborderons donc pas cette question ici.
Que ceux que le sujet intresse se rassurent cependant : ce chapitre existe, mais sous la forme dun
complment la fin du livre (chapitre 19) : vous y trouverez la version rseau de notre jeu de
bombardes.
En attendant, voyons tout de mme comment nous pouvons encore progresser, en apportant notre
projet quelques amliorations qui en feront un jeu pour 4 joueurs. Nous nous efforcerons aussi de
mettre en place une programmation bien compartimente, de manire ce que les mthodes de nos
classes soient rutilisables dans une large mesure. Nous allons voir au passage comment cette
volution peut se faire sans modifier le code existant, en utilisant lhritage pour produire de nouvelles
classes partir de celles qui sont dj crites.
Commenons par sauvegarder notre ouvrage prcdent dans un fichier, dont nous admettrons pour la
suite de ce texte que le nom est : canon03.py.

Jeu des bombardes

271

Nous disposons ainsi dun vritable module Python, que nous pouvons importer dans un nouveau
script laide dune seule ligne dinstruction. En exploitant cette technique, nous continuons
perfectionner notre application, en ne conservant sous les yeux que les nouveauts :
1#
2#
3#
4#
5#
6#
7#
8#
9#
10#
11#
12#
13#
14#
15#
16#
17#
18#
19#
20#
21#
22#
23#
24#
25#
26#
27#
28#
29#
30#
31#
32#
33#