Version en ligne

Tutoriel : Apprenez à programmer en Python (vidéo)

Table des matières

Apprenez à programmer en Python (vidéo)
Présentation et installation
Vidéo
Transcript
Les variables et les types de données
Vidéo
Transcript
Premier programme en Python
Vidéo
Transcript
Les listes
Vidéo
Transcript
Fonctions et méthodes (1/2)
Vidéo
Transcript
Fonctions et méthodes (2/2)
Vidéo
Transcript
Le contrôle de flux
Vidéo
Transcript
Un vrai programme : un quiz
Vidéo
Transcript
Fichiers et exceptions
Vidéo
Transcript
Algorithmique
Vidéo
Transcript
Créer une fonction
Vidéo
Transcript
Coder comme un pro
Vidéo
Transcript
Récursion (1/2)
Vidéo
Transcript
Récursion (2/2) : trier
Vidéo
Transcript
Objets et méthodes
Vidéo
Transcript
Objets encore...
Vidéo
Transcript
Objets toujours...
Vidéo
Transcript
Les éléments de l'interface graphique
Vidéo
Transcript
Réagir aux événements
Vidéo
Transcript
Gérer les erreurs
Vidéo
Transcript
Modifier l'apparence
Vidéo
Transcript
Boutons, menus et fichiers
Vidéo
Transcript
Les bases de données et le langage SQL
Vidéo
Transcript
Gestion d'une collection de films en Python et SQL
Vidéo
Transcript
Chargement de données en Python
Vidéo
Transcript

Apprenez à programmer en Python (vidéo)

Python est un des plus célèbres langages de programmation utilisés aujourd'hui. Plus récent que le langage C, il se veut aussi plus simple d'emploi et permet de créer tous types de programmes.

Python est utilisé dans de nombreuses entreprises (comme Google !) car il offre de nombreux avantages :

Ce cours vous propose de découvrir Python pas à pas sous forme d'une série de vidéos amusantes et accessibles à tous.

Bon visionnage !

Présentation et installation

Vidéo

Qu'est-ce que Python ?
D'où vient ce nom aussi saugrenu ?
Comment crée-t-on des programmes ?

Dans ce premier chapitre, je vais vous présenter tout ce qu'il faut savoir pour se préparer à utiliser Python. Nous allons installer les outils qui nous permettront de commencer à programmer en Python dès les prochains chapitres ! :)

Vidéo

Présentation et installation Transcript

http://www.dailymotion.com/swf/video/xbk6lj_tech


Présentation et installation Transcript

Transcript

Vidéo Les variables et les types de données

Les langages de programmation

Bonjour ! Je vais vous présenter un langage de programmation nommé Python. Un langage de programmation, vous savez probablement que c’est ce qui permet d’écrire les programmes qui permettent aux ordinateurs de faire des choses intéressantes. Ce que vous savez peut-être moins, c’est que des langages de programmation il y en a plusieurs. En fait, si vous cherchez sur Wikipédia la liste des langages de programmation, vous allez vous rendre compte qu’il n’y en a pas seulement plusieurs, mais beaucoup. Et quand je dis beaucoup, c’est vraiment beaucoup. Certains sont évidemment plutôt confidentiels. Mais même les « grands » langages de programmation sont très nombreux. Ce qu’il faut savoir, c’est qu’il y a, en simplifiant beaucoup, deux grandes familles de langages de programmation.

Pourquoi ? Pour les gens qui ne sont pas du métier, « informaticien » est une seule profession. Dans la réalité, il y a plein de métiers différents dans l’informatique, et parmi les gens qui sont amenés à programmer il y a deux catégories. D’un côté, les informaticiens qui font du développement, qui vont écrire des programmes qui vont du contrôle du système de freinage d’une voiture aux jeux de votre téléphone en passant par la gestion des caisses d’un supermarché. Et de l’autre côté, les informaticiens qui font ce qu’on appelle du système et qui s’assurent que les programmes écrits par les autres se déroulent bien et que les ordinateurs fonctionnent correctement. C’est comme dans l’industrie ou le bâtiment, il y a d’un côté les ingénieurs des bureaux d’études, et de l’autre les ingénieurs de production qui s’assurent que les usines fonctionnent le mieux possible et qui cherchent à anticiper et résoudre tout ce qui peut aller de travers. Quand on vous dit « il y a un problème informatique », ce sont les gens du système qui travaillent dur pour essayer de le régler.

Langages de script et langages compilés

Quand les gens du développement écrivent un programme, ils tapent un texte dans un langage donné, qu’ils passent par une moulinette qui produit un fichier exécutable. Le programme moulinette s’appelle un compilateur. Le fichier exécutable, comme tout fichier sur un ordinateur, est composé de 0 et de 1. Mais on ne peut pas l’ouvrir avec un traitement de texte ou d’image ou de son – il est composé de commandes que la machine sait directement transformer en signaux électriques qui vont faire faire certaines choses au matériel. Cela veut dire qu’un fichier exécutable est très dépendant du matériel (et aussi du système d’exploitation, c'est-à-dire les programmes qui se lancent automatiquement quand on allume la machine). On ne peut pas copier un programme compilé d’un PC sous Windows vers un Mac ou un PC sous Linux – c'est-à-dire qu’on peut le copier mais il ne marchera pas.

Les langages de programmation qui utilisent un compilateur n’intéressent pas vraiment les ingénieurs système. Les programmes qui les intéressent, ce sont des programmes qui vont enchaîner des tâches sur un ordinateur, qui vont vérifier avant de lancer un programme que celui dont il dépend s’est bien passé, qu’il y a assez de place sur un disque avant d’y copier quelque chose, et qui vont permettre d’automatiser des tâches qui seraient très ennuyeuses à faire à la main. Les langages de programmation qu’ils emploient sont appelés langages de script, on parle aussi souvent de shell (qui veut dire coquille en anglais). Python appartient à cette famille. Par rapport aux autres langages, il y a un programme moteur qui est compilé et dépend de la machine, mais ce programme lit et interprète des commandes que l’on peut copier de machine à machine. Autrefois, les langages de script étaient très limités. Mais aujourd’hui, ce sont les couteaux suisses de l’informatique et ils permettent de faire facilement beaucoup de choses – par exemple, envoyer un SMS à un ingénieur système responsable d’un système critique s’il y a un gros problème – je ne suis pas certain que c’est exactement le genre de message qui sera envoyé, mais vous voyez l’idée.

Ce qui a beaucoup contribué au succès des langages de script, ce sont les sites Web. Sur la plupart des sites Web, la magie est réalisée par des programmes en langage script, tels que Python, mais aussi d’autres tels que Ruby, PHP ou Perl entre autres.
Python a été inventé par cet informaticien. Il est néerlandais, et s’appelle Guido van Rossum. Il a distribué Python gratuitement, et comme le langage était plutôt facile à apprendre et était plein de bonnes idées il a eu un énorme succès, van Rossum est devenu une vedette et a fini par se faire embaucher par une société très connue qui utilisait beaucoup Python. Je pense que vous pouvez lire son T-shirt comme moi.

Pourquoi le langage s'appelle-t-il "Python" ?

Pourquoi Python ? C’est une référence à Monty Python, un groupe de comiques anglais qui a eu énormément de succès et d’influence dans les années 1970 at au début des années 80. Évidemment, si vous dîtes « Monthy Python » à un anglais il ne va rien comprendre, vous aurez davantage de chances avec « Monty Python ». Ce groupe était composé de 5 anglais, tous diplômés d’Oxford ou de Cambridge, et d’un Américain, Terry Gilliam, qui à sa décharge est aujourd’hui citoyen britannique. Vous connaissez peut-être John Cleese, qui joue Q dans certains films de James Bond. Van Rossum est un fan de leur humour déjanté (si vous êtes bon en anglais, il y a plein d’échantillons sur Youtube), et dans la documentation en anglais du langage il y a beaucoup d’exemples basés sur des sketches des Monty Python.
Beaucoup de gens pensent plutôt au serpent parce qu’un éditeur informatique très connu aux États-Unis, O’Reilly, est célèbre pour ses couvertures avec des gravures d’animaux. Évidemment pour Python ils n’allaient pas utiliser un canari.

Installer Python

Après cet intermède culturel, comment avoir Python sur sa machine ? Si votre machine tourne sous Linux, il y a de très fortes chances qu’il soit déjà présent. C’est très facile à vérifier : vous tapez « python » et si ce qui se passe c’est cela, tout va bien. On quitte Python en appuyant simultanément sur les touches Ctrl et D.

Si vous n’avez pas Python sur votre machine, ce qui est plus probable si vous avez un ordinateur sous Windows, il va falloir l’installer. C’est très facile si vous êtes connectés à Internet. Il suffit d’aller visiter le site python.org. Là, vous cliquez sur « Download », et vous cliquez sur la version qui correspond à votre machine. Vous sauvegardez, c’est le plus long, j’abrège, puis, pour Windows, vous allez rechercher le programme téléchargé et vous le lancez. Je vais changer le répertoire d’installation parce que j’aime bien tout avoir sous « Program Files », mais autrement il n’y a qu’à cliquer sur « next ». J’abrège encore, c’est fini.

Direction le menu, on trouve Python et l’on clique sur ce qui s’appelle IDLE. Et là, on se retrouve avec quelque chose qui ressemble en mieux à ce qu’on avait sous Linux. Pour sortir, c’est pareil que sous Linux, on appuie simultanément sur CTRL et D.
Voilà, pour l’installation c’est fini, nous sommes prêts à faire nos premiers pas avec Python.


Vidéo Les variables et les types de données

Les variables et les types de données

Transcript Vidéo

Après avoir découvert ce qu'est Python et comment l'installer sur sa machine, nous allons faire nos premières manipulations avec le langage dans ce chapitre.

Nous découvrirons le concept des variables, ces fameuses "boîtes" qui permettent de retenir des nombres et du texte en mémoire.
Nous effectuerons aussi des opérations mathématiques (oh, très simples rassurez-vous !) et comment fonctionne la division entière en Python.
Enfin, nous apprendrons aussi à stocker du texte en mémoire grâce aux variables.

Ce chapitre présente vraiment la base de la programmation Python, donc soyez attentifs !

Vidéo

Les variables et les types de données Transcript

http://www.dailymotion.com/swf/video/xbk6nq_tech


Les variables et les types de données Transcript

Transcript

Vidéo Premier programme en Python

Lancement du Python GUI

Pour commencer à travailler avec Python nous allons d’abord, sous Windows, aller cliquer sur Démarrer, puis « tous les programmes » et là rechercher Python. Le programme à lancer s’appelle IDLE et il est indiqué comme « Python GUI », G-U-I, qui se dit d’habitude gooey, est l’acronyme de Graphical User Interface, Environnement Utilisateur Graphique. Ce n’est pas très graphique, mais c’est assez pratique.

Tant que j’en suis dans les acronymes, vous vous demandez peut-être ce qu’IDLE veut dire. Dans l’informatique professionnelle, on voit souvent IDE qui veut dire environnement intégré de développement. IDLE est un double jeu de mots, parce que d’une part IDLE veut dire oisif, sans rien à faire en Anglais, et comme vous allez le voir le programme passe son temps à attendre que vous passiez des commandes, et d’autre part parce que l’un des Monty Python s’appelle précisément Eric Idle. On reste bien dans le même esprit… Quand on lance IDLE on a toujours un message légèrement alarmiste qui vise essentiellement à dire qu’il ne faut pas s’alarmer.

En début de ligne, on a trois signes supérieur qui indiquent que IDLE attend que l’on fasse quelque chose.

Premiers pas avec les variables

Je vais taper ma première commande Python, boite = 32, et appuyer sur Entrée. Et là, il ne se passe rien …

Qu’est-ce que « boite = 32 » veut dire ? Tout d’abord, l’égalité n’a rien à voir avec les maths. Quand je tape « boite », qui est un nom complètement arbitraire, j’aurais pu choisir n’importe quoi qui commence par une lettre et est suivi de lettres, de chiffres ou de caractères comme souligné, je crée « une boîte » - en informatique, on dit plutôt une variable. Le signe = indique que je mets quelque chose dedans, le terme correct est « affectation », et 32 est la valeur que j’y mets. Dans la réalité des choses, et je vous explique cela parce que cela aura son importance plus tard, ce que fait Python c’est qu’il crée une boîte, marque « 32 » quelque part dans la mémoire, et indique dans la boîte où se trouve ce qu’on y a mis.

L’immense majorité des langages de programmation fonctionne par ce qu’on appelle des mots-clefs, qui sont des mots anglais qui ont une signification particulière dans le langage. Si vous êtes nul en Anglais ne vous inquiétez pas, ces mots ne sont pas très nombreux et en général assez simples ; ne comptez pas sur l’informatique pour vous faire faire des progrès dans la langue de Shakespeare (et des Monty Python).

Un de ces mots clefs est « print », qui veut dire « imprime » ou « affiche ». Si je tape print boite Python affiche ce que j’ai mis dans « boite ». Tout va bien. Au passage, vous remarquerez que quand Idle reconnaît un mot-clef il en change la couleur ; si ce n’est pas dans la bonne couleur, c’est qu’il y a une faute de frappe.

De plus en plus fort : boite2 = boite. Cela veut dire que je prends ce qu’il y a dans boite, et que je le recopie dans boite2. Là, de manière interne, Python ne va pas recopier la valeur 32 mais va simplement indiquer que ce qu’il y a dans boite2 se trouve au même endroit que ce qu’il y a dans boite.

J’affiche le contenu de boite, je vois 32, j’affiche le contenu de boite2, je vois aussi 32.

Opérations sur les variables

Maintenant je vais changer le contenu de boite, et je vais dire boite = boite / 2. En clair, je prends le contenu de boite, je le divise par 2, et je le remets dans boite. J’imprime boite, et boite contient 16.

Que s’est-il passé dans notre dos ? Python a pris la valeur vers laquelle pointait « boite ». Il ne modifie jamais les valeurs numériques qu’il range en mémoire. Il a donc fait son calcul, trouvé une nouvelle valeur qu’il a rangé ailleurs, et fait pointer boite sur la nouvelle valeur. Ce qui est important, c’est que pour boite2 rien n’a changé.
Et si maintenant je demande à afficher le contenu de boite plus le contenu de boite2, je trouve bien 48, la somme de 16 et de 32.

La division entière

Nouvel essai. Je mets une autre valeur dans boite, 31. Je la divise par 2, j’affiche, et là j’obtiens 15 – pas 15 et demi. Ooops. Qu’est-ce qui se passe ?

Ce qui se passe, c’est que Python devine la nature, on dit le type, des données que l’on stocke dans les variables. Je lui ai mis 31, et il a reconnu un entier – en anglais on dit « integer » - je vous dit ça parce que cela va nous servir bientôt. Et à partir du moment où l’on a des entiers, eh bien il fait des opérations entières, et en particulier des divisions entières. Il faut se rappeler que les langages de script comme Python étaient à l’origine l’outil de base des informaticiens du système, et qu’ils travaillent avec des fichiers, des disques informatiques, et tout un tas de choses que l’on ne peut pas diviser ; même quand il parlent de temps, ils ne vont pas parler d’une demi minute mais de 30 secondes. Dans cet environnement, ne travailler qu’avec des entiers est parfaitement logique.

Mais tout n’est pas fichu pour les matheux. Au lieu de taper boite = 31, je vais taper boite = 31 point. Le point, si vous utilisez une calculatrice vous le savez déjà, est aux Anglo-Saxons ce que la virgule est aux Français ou aux Belges. J’aurais pu mettre 31.0, mais 31 point suffit. Je redivise par 2, j’affiche, et là j’ai bien 15 et demi.
Comme tout à l’heure, Python a deviné ce que je mettais, il a vu le point, il s’est dit c’est du décimal (en anglais informatique on dit float, « flottant », parce que le point décimal peut « flotter » parmi les chiffres), et à partir de ce moment là il a effectué une division décimale.

Mais j’aurais pu aussi faire cela : mettre un entier dans boite, et diviser par 2 point : j’aurais encore retrouvé 15 et demi pour résultat. En fait, quand on mélange dans une opération entiers et décimaux, le résultat est décimal. Avec des entiers, le résultat est entier.

Cela peut amener des erreurs assez sournoises, et je vais vous en donner un exemple. Supposons que je commence par mettre 5 dans ma variable boite. Je divise d’abord boite par 2 point, ce qui me donne 2 et demi, puis par 3 point, et le résultat, correct, est 0.83333333 …

Mais si dans la parenthèse j’oublie le point après le 2, je fais entre parenthèses une division entre deux entiers, 5 et 2, et donc une division entière qui me donne 2. Et quand je redivisise par 3 point, j’effectue la division décimale de 2 par 3, et j’obtiens 0.666666 … Le résultat est décimal, a l’air juste, mais est probablement faux par rapport à ce que je voulais faire parce que quelque part dans mes calculs s’est glissée une opération entière. Il faut donc faire très attention. J’aurais évité tout problème avec 5 point dans ma variable au départ.

Les variables et le texte

Les chiffres, c’est bien gentil, mais peut-on mettre du texte dans une boîte ? Si je tape boite = bonjour je déclenche un torrent d’injures de la part de python. L’explication est là, ‘name bonjour is not defined’, le nom bonjour n’est pas défini – python a cru que « bonjour » était le nom d’une boite, qui n’existe pas puisque les boites sont créées quand on met quelque chose dedans.

Pour dire à python que ‘bonjour’ est du texte et pas le nom d’une boîte, il suffit que je le mette soit entre apostrophes, soit entre guillemets – le résultat est le même. Si dans le texte j’ai une apostrophe je le mets entre guillemets et réciproquement. IDLE change automatiquement la couleur à vert. Quand j’affiche le contenu de ma variable, il n’y a pas les apostrophes ou guillemets : ils ne sont là que pour dire à python la nature de ce que je mets dans la boite.

Voilà pour nos tous premiers pas dans Python. Pour sortir d’IDLE, rappelez vous qu’il suffit d’appuyer en même temps sur les touches CTRL, en bas à gauche ou à droite du clavier, et D.


Vidéo Premier programme en Python

Premier programme en Python

Transcript Vidéo

Nous allons maintenant aller un peu plus loin dans notre exploration de Python et créer notre premier vrai programme.

Pour y parvenir, nous allons faire appel à nos premières fonctions et inviter l'utilisateur à participer au programme en saisissant son nom.
Nous apprendrons à reconnaître les différents types de variables et à convertir un texte en nombre.

A la fin de ce chapitre, nous aurons réalisé un programme qui demande à l'utilisateur son nom et lui dit bonjour !
Ce ne sera pas encore un programme très impressionnant, mais patience... Nous commençons tout juste ! ;)

Vidéo

Premier programme en Python Transcript

http://www.dailymotion.com/swf/video/xbk6oz_tech


Premier programme en Python Transcript

Transcript

Vidéo Les listes

Les fonctions

Nous avons vu jusqu’à présent comment mettre des entiers, des nombres décimaux ou du texte (on dit chaîne de caractères en informatique) dans une boîte. L’expression correcte est « nous avons vu comment affecter à des variables des valeurs de différents types ».

L’affectation avec égal, c’est bien gentil, mais un programme demande en général une certaine interaction de l’utilisateur – par exemple, le programme va demander à l’utilisateur de rentrer des valeurs. Pour cela on utilise ce que l’on appelle une fonction informatique. En informatique, une fonction a un nom, qui est suivi de parenthèses entre lesquelles on donne en général un ou plusieurs paramètres séparés par des virgules, et retourne une valeur que l’on affecte à une variable.

La fonction raw_input : demander à l'utilisateur de saisir un texte

Voyons tout de suite un exemple : dans boite, je vais mettre le résultat de la fonction raw_input à laquelle je passe un paramètre, le texte « Que met-on dans la boite ? « raw_input signifie « entrée brute », et c’est la fonction que l’on utilise pour récupérer dans un programme python ce qui est tapé au clavier. Si j’appuie sur « Entrée » ce qui est affiché c’est le paramètre que j’ai donné à la fonction, et le programme attend – je n’ai pas les trois > que j’ai d’habitude. Je vais taper « N’importe quoi » - vous remarquerez qu’avec l’apostrophe IDLE se mélange dans les couleurs mais ce n’est pas grave. Une autre chose à remarquer, c’est que dans ma chaîne j’ai mis un espace avant l’apostrophe fermante. Je le retrouve ici. Sans cette espace, quand j’aurais tapé le N se serait retrouvé collé au point d’interrogation. Cela aurait marché tout aussi bien, mais ça aurait fait un peu moche.

J’appuie sur <Entrée>, je retrouve mes trois signes supérieur, et IDLE attend une commande (et ce n’est plus mon programme qui attend une donnée).
J’affiche le contenu de ma variable boite, et bien entendu je retrouve ce que j’ai entré.

Vous pouvez remarquer qu’ici je N’AI PAS mis mes données entre guillemets. La raison est qu’il n’y avait pas d’ambiguïté : le programme attend des données. Si j’avais tapé le nom d’une autre variable, j’aurais retrouvé non pas le contenu de la variable mais simplement son nom dans boite.
Mais cela veut dire que l’on peut avoir des soucis, et je vais vous montrer lesquels.

Les valeurs numériques et le texte

Je vais entrer une valeur numérique dans une variable que j’appelle x. Je rentre 3, et je veux afficher trois fois la valeur entrée. Ooops ! Problème. Un autre exemple pour que vous compreniez ce qui se passe : quand on multiplie du texte, on l’affiche autant de fois qu’on le multiplie. La boîte x contient le TEXTE 3. Raw_input retourne toujours du texte. Écrire x = raw_input() est comme si on écrivait x = ouvrez les guillemets.

Si je veux avoir un vrai nombre dans ma variable, il faut que je le dise. Rappelez vous qu’un entier se dit integer en anglais. Il y a une fonction int() à laquelle on passe du texte et le transforme en entier. Si donc je tape x = int(raw_input(‘Entrez la valeur de x’)) en faisant bien attention à fermer toutes les parenthèses, quand j’imprime trois fois x, ce coup ci le résultat est plus normal.

Convertir du texte en nombre et inversement

Il y a donc des fonctions pour convertir des types. Int() prend la représentation sous forme de texte d’un entier pour en faire un vrai entier avec lequel on peut faire des calculs. Je vous ai dit qu’en informatique on appelait souvent les nombres décimaux « float », il y a une fonction float qui prend la représentation sous forme de texte d’un nombre décimal – attention encore une fois, il faut un point et non pas une virgule – et en fait aussi un vrai décimal.

Il y a aussi la fonction str() (abréviation de « string », rien à voir avec les sous-vêtements, je suis désolé de vous décevoir, cela vient de « character string » qui veut dire « chaîne de caractères »). Str() effectue l’opération inverse de int() et de float(), elle prend un nombre entier ou décimal et le transforme en texte.

C’est beaucoup plus utile que vous ne pouvez le penser. Si dans un programme on fait des calculs, on aime bien en général en afficher le résultat avec un texte explicatif. Vous avez vu que multiplier du texte le répétait, quand on additionne du texte on le colle bout à bout. Si l’on a une variable numérique et si on essaie de la coller à du texte on a une erreur, qui dit que l’on ne peut pas concaténer, c'est-à-dire coller bout à bout, une objet de type str (du texte) et un objet de type int (un entier).
La solution est d’utiliser la fonction str() pour convertir le nombre en texte et pouvoir afficher le message.

Je peux aussi appliquer int() à un nombre décimal, ce qui me donne sa partie entière. Mais si j’applique int() à du texte qui représente un nombre décimal, j’ai une erreur et cela ne marche pas. En revanche, ce que je peux faire c’est appliquer float() au texte, obtenir le résultat décimal et lui réappliquer int(). Bienvenue dans le monde tordu des informaticiens.

Écriture de notre premier programme

Il est maintenant temps de voir comment nous pouvons écrire notre premier programme, qui ne sera pas trop ambitieux. Jusqu’à présent vous avez vu un certain nombre de commandes, que l’on a passées, manuellement, les unes après les autres. Un programme, c’est comme un programme de télévision, cela dit ce qui va se passer, et dans quel ordre – cela enregistre des opérations à dérouler automatiquement. Sur un ordinateur, dès que l’on enregistre, on met dans un fichier. Nous allons donc écrire nos commandes dans un fichier.

Pour cela, il faut aller dans le menu dans « File », qui signifie Fichier, puis « New Window » pour ouvrir une nouvelle fenêtre. Là, nous allons pouvoir écrire un programme ; nous sommes dans un environnement comme Notepad, sauf qu’il comprend les mots clefs et tout le reste et change les couleurs pour nous aider à détecter les fautes de frappe. Je commence par une ligne qui commence par le signe dièse – c’est un commentaire, Python ne le regarde pas, c’est pour expliquer le programme. Il est fréquent que plusieurs personnes améliorent tour à tour un programme. Comme la logique est parfois assez compliquée, il faut mettre des explications pour que d’autres comprennent et puissent reprendre après nous.

Je vais créer une variable « nom » dans laquelle nous allons mettre le nom de l’utilisateur du programme, puis nous allons afficher « Bonjour » suivi du nom. Pas vraiment palpitant comme programme, ce n’est pas avec cela que vous aller vous faire embaucher par Google, mais c’est quelque chose que l’on voit sur presque tous les sites web …

Lancement du programme

Nous allons dans le menu cliquer sur « Run », puis « Run module » pour lancer le programme. Là, protestation d’IDLE : il faut, comme vous l’avez vu, écrire d’abord le programme dans un fichier. On dit donc que l’on veut le sauver, je vais créer le fichier sur le bureau, et je vais l’appeler premier_programme.py. C’est important de terminer par .py parce que Windows a besoin de l’extension pour connaître le type du programme et faire ce qui va bien quand on clique dessus. Ensuite le programme se lance sous IDLE, enchaînant automatiquement les commandes, et tout va bien.

Allons maintenant sur le bureau cliquer sur le programme que nous avons créé. Une fenêtre se lance, tout va bien, je rentre un nom, et là catastrophe la fenêtre se ferme sans rien avoir affiché ! Le programme ne marche pas !

En fait, c’est faux : le programme a bien affiché quelque chose, mais si vite que l’on n’a pas eu le temps de voir.

Je vais donc reprendre le programme, et rajouter un appel à raw_input. J’appelle la fonction sans en affecter le résultat à une variable, parce que je me fiche complètement du résultat : tout ce que je veux, c’est que le programme pause avant de se terminer. Je sauve, et là un message bizarre : Non-ASCII found. Qu’est-ce que ce truc veut dire ?

Le standard ASCII

Je vais vous l’expliquer : ce que je viens de mettre et qui ne plaît pas à Python, c’est l’accent aigu de « Entrée ». ASCII, cela veut dire American Standard Code for Information Interchange, c’est un standard qui a pas loin de 50 ans et c’est bêtement ce qui définit comment les lettres de l’alphabet sont codées dans un ordinateur. En anglais, il n’y a pas d’accent, et les américains se sont bornés à définir les codes pour les majuscules, les minuscules, les chiffres et les signes de ponctuation. Évidemment, les Français, les Italiens, les Espagnols, les Allemands, et les Scandinaves n’ont pas été long à râler et réclamer leurs lettres à eux. Et puis les Russes s’y sont mis, les Arabes, les Chinois, les Japonais, les Indiens …

Le problème, c’est que les standards pour coder les caractères autres que ceux qui sont dans le code ASCII, il y en a plusieurs – et Python veut savoir lequel, et il faut le préciser dans un commentaire. Il propose par défaut cp1252, qui est le code habituel sous Windows. D’autres possibles pour le français sont latin-1 ou utf8. Je clique sur « Edit my file » pour corriger, très gentiment IDLE m’insère la ligne qui va bien, je sauve de nouveau, pas de problème, je ferme IDLE et je lance le programme en cliquant dessus, on me demande mon nom, je rentre n’importe quoi, MON MESSAGE EST AFFICHE, et je vois que, malgré l’indication du code, le é de Entrée ne sort pas bien. Moralité : si vous voulez lancer votre programme sous Windows, évitez les accents, un des grands sujets casse-pieds de l’informatique.. Il n’y a pas de problème dans IDLE.


Vidéo Les listes

Les listes

Transcript Vidéo

Après les variables simples, voyons maintenant les listes et les opérations que l'on peut leur appliquer : ajouter ou enlever des éléments, en extraire des morceaux, parcourir tous les éléments…
Et au passage, nous découvrirons une différence fondamentale entre la modification d'une variable simple et celle d'une liste.

Vidéo

Les listes Transcript

http://www.dailymotion.com/swf/video/xbk6sd_tech


Les listes Transcript

Transcript

Vidéo Fonctions et méthodes (1/2)

Bonjour,
Vous avez vu dans les pythonneries précédentes que l’on pouvait facilement créer une variable et y mettre une valeur, entière, décimale ou texte. Par exemple je peux créer une variable que j’appelle « nom » en y mettant le texte ‘Maggie’.

Présentation des listes

Mais on peut créer d’autres variables que des variables qui contiennent une seule valeur. Par exemple, on peut créer des listes. Pour créer une liste, comme pour une variable, je lui donne un nom, puis je précise par le signe = que je mets des valeurs dedans, et je donne une liste d’éléments séparés par des virgules entre crochets. Ce sont les crochets qui disent à python que je crée une liste. Les éléments de la liste peuvent être de n’importe quel type, on peut mélanger les types, ce que je vous ne recommande pas, et un élément d’une liste peut même être une liste – je crois que l’on attendra d’être à un numéro de pythonnerie un peu plus élevé avant de voir ce à quoi cela peut servir.
Restons simples, et contentons-nous de cette liste de noms. On peut créer une liste avec autant d’éléments que l’on veut, on peut même créer une liste vide, uniquement composée d’un crochet ouvrant et d’un crochet fermant.

Une variable était équivalente à une boîte ; une liste est équivalente à une pile de boîtes, et ces boîtes sont numérotées exactement de la même manière que les étages dans les ascenseurs en France : on commence au zéro, et le dernier élément a pour numéro le nombre d’éléments de la liste moins un. C’est toujours un peu perturbant de se dire que le premier élément de la liste est l’élément numéro zéro. J’espère qu’en pensant aux ascenseurs vous vous tromperez moins.

Reprenons donc notre liste, composée des noms Bart, Homer, Marge et Lisa. Tout comme pour une variable simple, je peux utiliser print pour demander à Python d’afficher à l’écran le contenu de la liste. Il me montre alors tous les éléments entre crochets.

La numérotation des éléments

Mais comme les boîtes de ma pile de boîtes sont implicitement numérotées, je peux également lui demander d’afficher ce qu’il y a dans la boîte n°2 – et là, il va m’afficher « Marge ». Si vous pensiez voir « Homer », c’est que vous avez déjà oublié que les boîtes sont, comme les étages, numérotés à partir de 0, et que la boîte n°2 contient le 3ème élément de la liste.

Si je demande à Python d’afficher une boite avec un numéro supérieur au nombre d’éléments dans la liste, j’ai une erreur qui me dit « list index out of range », c'est-à-dire « indice (autrement dit numéro) de liste en dehors des bornes permises ». Normal.

Je peux faire mieux que d’afficher toute la liste ou seulement un élément. Je peux afficher une sous-liste, par exemple je peux dire d’afficher les éléments 1 à 3 ; les deux points indiquent que c’est un intervalle de valeurs que je veux. Dans ce cas j’obtiens pour résultat « Homer, Marge ». Par rapport aux éléments de ma liste, on voit que j’ai le contenu des boîtes 1 et 2. En clair, la valeur minimale est incluse dans ce que je retourne, et la valeur maximale exclue. En notation mathématique, l’intervalle est ouvert du côté supérieur, et c’est quelque chose dont il faut se souvenir si l’on ne veut pas de surprises.

Il est aussi possible d’omettre l’une des bornes, ce qui veut dire « tout jusqu’à … exclu » ou « tout à partir de … » suivant la borne que l’on omet. Ainsi, afficher liste crochet deux points deux crochets veut dire afficher tous les éléments jusqu’à 2 exclu, donc les éléments 0 et 1.

On peut même ne mettre que les deux points pour dire « tous les éléments ». Si j’affecte à liste2 liste crochet deux points crochet et si j’affiche liste2, je me retrouve avec un contenu identique à celui de liste.

J’ouvre une parenthèse rapide : la notation avec les crochets et deux points s’applique en python à autre chose que des listes. Par exemple, il considère le texte un peu comme si c’était des listes de lettres. Si je demande à afficher ‘TEXTE’ crochet deux points deux crochet, j’obtiens TE, les lettres 0 et 1 du mot. Bien entendu, cela aurait marché tout aussi bien si j’avais utilisé à la place d’un mot entre apostrophes le nom d’une variable contenant du texte.

Manipulation des éléments de la liste

Fermons la parenthèse et retournons aux listes.
Je peux utiliser la notation numérotée pour autre chose qu’afficher les éléments d’une liste. Si vous vous rappelez, tout à l’heure j’ai mis « Maggie » dans la variable « nom ». Je vais maintenant remplacer l’élément n°2 de la liste par le contenu de « nom ».

Avant de faire cela, je vais faire ce que je faisais dans la pythonnerie n°2 avec mes variables boite et boite2, à savoir que je vais créer une nouvelle variable simpson dans laquelle je mets liste. Si j’affiche simpson, je trouve bien ce à quoi je m’attends, le comportement est le même qu’avec les variables simples.

Maintenant je vais utiliser les éléments numérotés de liste comme si c’étaient des variables simples, et je vais mettre dans l’élément n°2 le contenu de la variable nom. J’affiche liste, et je vois que là où j’avais Marge j’ai maintenant Maggie. Mais si j’affiche « simpson », je retrouve aussi Maggie à la place de Marge. Ce n’est pas du tout le même comportement qu’avec les variables simples, où quand je divisais le contenu de boite par 2 le contenu de boite2 restait inchangé.

Mais je n’ai pas fini. Si vous vous rappelez, il y a quelques instants j’ai écrit liste2 égale liste crochet deux points crochets, et je vous ai montré que dans liste2 j’avais la totalité des éléments de liste. Que vaut liste2 maintenant ? Eh bien quand je l’affiche il n’a pas bougé et contient toujours « Marge » en position numéro 2.
Tout ceci est important et demande un brin de discussion.

Je vous ai dit dans la pythonnerie n°2 quand quand je disais boite2 = boite je faisais en fait pointer boite2 vers le même endroite en mémoire que boîte. Sauf que derrière, quand j’ai divisé le contenu de boîte par deux, python n’a pas changé la valeur 32, mais simplement écrit 16 ailleurs et fait pointer boite vers la nouvelle valeur, en laissant boite2 toujours pointer vers 32 ; Python ne modifie pas les entiers, les décimaux ou le texte : quand vous en changez la valeur, il va juste ranger la nouvelle valeur ailleurs sans toucher à l’ancienne. Avec les listes c’est différent. Au début j’avais simplement liste. Quand j’ai passé la commande liste2 = liste crochet deux points crochet, j’ai créé une nouvelle liste dans laquelle j’ai recopié une à une toutes les valeurs qui étaient dans liste. Quand j’ai écrit simpson la liste simpson était simplement un synonyme qui pointait vers les mêmes valeurs, comme avec les boites. Sauf que quand j’ai changé Marge en Maggie, Python a vraiment changé le contenu de la liste (en fait, les éléments sont comme des variables et l’élément n°2 a pointé vers une nouvelle valeur) : liste et simpson ont été modifiées, et pas liste2 qui avait été copiée avant la modification.

C’est parfois très utile d’avoir des synonymes, et c’est parfois très gênant : il faut juste savoir ce que l’on veut faire, et utiliser de « vraies » copies si c’est nécessaire.

Affichage un à un des éléments de la liste

Pour terminer cette brève introduction aux listes, quelque chose de très important sur lequel j’aurai l’occasion de revenir : pour opérer sur les éléments d’une liste, par exemple pour les afficher, on peut tous les afficher ensemble, ou par élément en précisant lequel. On peut aussi tous les afficher, mais un par un, avec une commande TRÈS importante, qui est la commande for, qui signifie pour. Je tape for, suivi du nom d’une variable qui n’a pas besoin d’exister, suivi de in, qui veut dire dans, puis du nom de ma liste, puis de :. Cela signifie que je veux mettre successivement dans ma variable element l’élément 0 de la liste, puis le 1, et ainsi de suite jusqu’à la fin. Quand je passe à la ligne, Python n’affiche pas les trois supérieurs habituels, mais me décale et attend une suite de commandes qui seront répétées pour chaque élément de la liste. On appelle un ensemble de commande répétées une boucle. J’affiche ‘Je suis dans la boucle’, je passe à la ligne python attend toujours une commande, j’affiche le contenu de ma variable élément, je passe à la ligne Python attend toujours quelque chose mais j’ai fini et je passe de nouveau à la ligne, ce qui lui fait afficher cela. On voit que mes commandes ont été répétées autant de fois qu’il y avait d’éléments dans la liste. En fait, python a parcouru liste, a recopié successivement chaque élément de la liste dans ma variable, et a exécuté mes commandes avec cette valeur. Voilà, c’était bref, mais je vous rassure, on reparlera des boucles …


Vidéo Fonctions et méthodes (1/2)

Fonctions et méthodes (1/2)

Transcript Vidéo

Les fonctions sont un élément incontournable des programmes informatiques. On peut les voir comme des outils qui effectuent des calculs et des actions pour retourner un résultat.

Nous verrons ici les fonctions qu'il faut absolument connaître pour modifier et afficher les données d'un programme, et des fonctions assez spéciales qui sont "accrochées" à une variable - qu'on appelle des méthodes (nous en reparlerons plus loin), qui sont illustrées par différents traitements que nous pouvons appliquer à du texte.

Vidéo

Fonctions et méthodes (1/2) Transcript

http://www.dailymotion.com/swf/video/xbk6vl_tech


Fonctions et méthodes (1/2) Transcript

Transcript

Vidéo Fonctions et méthodes (2/2)

Bonjour,

Je vais maintenant vous parler des fonctions et des méthodes dans Python et vous présenter certaines des plus communes. Vous avez déjà vu quelques fonctions, comme raw_input(), ou comme int(), float() et str() qui permettent de change le type des données. Une fonction est une opération que l’on applique en général à une donnée (ce n’est pas le cas de raw_input(), qui est un peu spéciale), qui peut éventuellement prendre d’autres paramètres qui permettent de légèrement modifier l’opération que l’on applique, et qui au bout du compte retourne une donnée.

Les retours des fonctions

Retourner une donnée, cela veut dire que si je dis :

variable = fonction(truc)

Je retrouve dans variable quelque chose que je peux afficher, qui est le résultat de la fonction, ou encore le résultat « retourné par la fonction » (vous verrez plus tard que vous pouvez écrire vos propres fonctions, et vous comprendrez mieux j’espère à ce moment là pourquoi on emploie ces expressions).

Quand je dis « une donnée », c’est parfois une liste. Assez souvent, ce qui est retourné n’est pas du même type que ce que l’on a passé à la fonction ; c’est évident avec les fonctions de conversion, mais il y a beaucoup d’autres exemples.

Une chose à laquelle il faut faire attention avec les fonctions, c’est que ce qu’elles retournent n’a pas toujours la même signification, suivant ce à quoi l’opération s’applique. Un très bon exemple est la fonction qui s’appelle len(), L-E-N, qui est l’abréviation du mot anglais length qui signifie longueur.

Si vous appliquez len() à du texte :

Print len(‘Monty Python’)
12

Vous obtenez le nombre de caractères (l’espace compte).
Si vous appliquez len() à une liste :

>>> print len(['Chapman', 'Cleese', 'Idle', 'Jones', 'Gilliam', 'Palin'])
6
>>>

Vous obtenez le nombre d’éléments dans la liste.
Il y a d’autres fonctions qui peuvent s’appliquer à une liste ; str() par exemple. Si l’on a une liste :

>>> mousquetaires = ['Athos', 'Porthos', 'Aramis']
>>> un_pour_tous_tous_pour_un = str(mousquetaires)
>>> print un_pour_tous_tous_pour_un
['Athos', 'Porthos', 'Aramis']
>>>

Hmm ? Cela ne semble pas très concluant ? Eh bien en fait, si, on a bien là du texte « normal », et on peut le vérifier en affichant la longueur de la liste et de cette nouvelle variable (au passage, j’emploie pour la première fois l’affichage de plusieurs valeurs sur la même ligne en les séparant par une virgule) :

>>> print len(mousquetaires), len(un_pour_tous_tous_pour_un)
3 30
>>>

On voit que dans le premier cas la longueur est le nombre d’éléments, et dans le deuxième c’est la longueur du texte. En fait, quand on écrit print suivi d’une liste, Python affiche le résultat de la fonction str() appliquée à la liste … Je reconnais que cela peut être un peu perturbant.
Une fonction peut aussi retourner une liste – un bon exemple est la fonction range(), qui veut dire « étendue » ou « intervalle ». Par exemple :

>>> print range(5)
[0, 1, 2, 3, 4]
>>>

Range() retourne les valeurs de 0 au nombre passé en paramètre moins un. Il existe des variantes de range(), on peut donner par exemple une valeur de départ et une valeur de fin séparées par des virgules.
Vous vous demandez peut-être à quoi peut bien servir une fonction comme range() ? Je vais vous en donner juste un exemple assez idiot mais qui devrait vous en donner une bonne idée. Supposez que dans un programme vous souhaitiez effectuer un traitement un certain nombre de fois. Je vais affecter ce nombre de fois dans une variable, il pourrait être lu avec raw_input() :

>>> compte = 3
>>> for x in range(compte) :
        print('hip')

        
hip
hip
hip
>>>

Je me sens un peu obligé de taper cela

>>> print 'hourra'
hourra
>>>

Voilà juste quelques fonctions, en plus des fonctions de conversion que nous avons déjà vues.

Les méthodes

Mais j’ai parlé au début de fonctions et de méthodes. Qu’est-ce donc qu’une méthode ? Dans le contexte Python, une méthode n’est pas une manière réfléchie de procéder. Cela n’a rien à voir avec le « Discours de la méthode » de ce bon vieux Descartes. Au revoir, Descartes. En fait, une méthode ressemble beaucoup à une fonction, mais avec d’une part des différences assez subtiles d’application, et d’autre part de grosses différences dans l’écriture.

Commençons par les différences subtiles. Nous avons vu qu’une fonction comme int() pouvait s’appliquer aussi bien à des valeurs décimales dont elle prend la valeur entière, qu’à des chiffres sous forme de texte qu’elle transforme en nombre sur lequel on peut faire des calculs. Nous avons vu aussi que si on l’applique à du texte qui représente un nombre décimal on a une erreur, ce qui peut ne pas sembler très logique. Nous venons de voir que len s’applique à une liste ou à du texte (avec des nombres on a une erreur), et que les résultats obtenus n’ont rien à voir.

Avec les méthodes c’est différent. Une méthode est beaucoup plus « attachée » que les fonctions à un type particulier de variable. Il y a des méthodes, principalement, pour le texte et pour les listes parmi les objets que nous avons vus jusqu’à présent. Les méthodes pour le texte n’ont rien à voir avec les méthodes pour les listes. Et si beaucoup de méthodes retournent une valeur, ce n’est pas le cas de toutes : certaines méthodes font des choses mais sans retourner « un » résultat. Vous comprendrez tout de suite mieux quand je vous donnerai des exemples.
La différence la plus visible entre fonctions et méthodes c’est la manière dont on les tape dans un programme. Pour appeler une fonction, on l’a vu, on tape son nom, et des paramètres entre parenthèses. Pour appeler une méthode (on dit en général plutôt « invoquer » une méthode, ça fait plus chic), on tape le nom de la variable, un point, puis comme si c’était une fonction le nom et les paramètres entre parenthèses (ou parfois simplement des parenthèses). Par convention, la méthode s’applique à la variable à laquelle elle est attachée par un point.

Je vais commencer par vous donner quelques exemples utiles avec du texte (je garde les méthodes de listes pour la prochaine pythonnerie). Un des principaux soucis que l’on a en informatique, c’est l’utilisateur du programme.

On ne peut jamais être certain de ce qu’il va entrer, et il faut toujours essayer de normaliser ce qu’il tape pour pouvoir traiter les données plus facilement. Je vais ici affecter directement du texte à des variables, mais dans un vrai programme cela aurait plutôt été fait par raw_input. Mettons ‘napoléon’ tout en minuscules dans la variable « nom ». Si je veux afficher nom tout en majuscules, je lui applique la méthode upper(). Pour tout mettre en minuscules et la première lettre en majuscule, j’utilise capitalize().

Changeons de nom, et mettons ‘louis-philippe’, entré par un utilisateur légèrement perturbé. Pour tout mettre en minuscules, l’inverse de upper() en quelque sorte est la méthode lower(). Je peux essayer d’appliquer la méthode capitalize() aussi, mais là j’ai un problème : il n’y a que la toute première lettre qui soit mise en majuscules. Si je veux bien afficher des prénoms composés, il y a mieux : il y a la méthode title(), qui veut dire titre, ainsi nommée parce qu’il est fréquent dans les livres américains de coller une majuscule à tous les mots d’un titre. Title() met en majuscule toute lettre qui n’en suit pas une autre., et en minuscule toute lettre qui suit une autre lettre.
Voilà maintenant une méthode très utile. Assez souvent les utilsateurs ajoutent des espaces, soit parce qu’ils hésitent, soit parce que leurs doigts s’accrochent sur le clavier. Je simule cela en tapant « oups j’ai tapé plein de blancs » et j’en mets aussi à la fin. J’affiche entre crochets pour que l’on voie bien qu’il y a des espaces dans ma variable.

Même si vous n’avez que des connaissances très rudimentaires de l’anglais, je suis à peu près certain que le verbe strip, qui veut dire enlever ou déshabiller, vous est vaguement familier. Avec python, la méthode strip() enlève les espaces au début et à la fin d’un texte. Nous pouvons le vérifier tout de suite :

C’est quelque chose de très utile pour nettoyer ce qui est entré par les utilisateurs. Enfin, et je m’arrêterai là pour les méthodes qui s’appliquent à du texte, il y a une autre méthode très utile qui transforme du texte en liste. Le mot à retenir est split (un verbe irrégulier) qui veut dire « fendre ». Je mets dans la variable comptage one, two, three, four en séparant mes nombres épelés par des virgules.

Contrairement aux méthodes que nous avons vues jusqu’à présent, split prend un argument, qui est le texte qui sert à séparer les différents éléments de la liste que je veux produire. Cela pourrait être n’importe quoi. Ici je dis que c’est une virgule, et j’affiche le résultat de la méthode appliquée à ma variable :

>>> print comptage.split(',')
['one', 'two', 'three', 'four']

Je peux vérifier que j’ai bien une liste en appliquant le test habituel de la fonction len.

>>> print len(comptage.split(','))
4

J’ai bien fabriqué une liste de 4 éléments.

Une chose très importante, c’est la combinaison. Je viens juste d’appliquer une fonction au résultat d’une méthode.
On peut aussi appliquer une fonction au résultat d’une fonction, je vous l’ai déjà montré en appliquant la fonction int() au résultat de la fonction float().
Avec les méthodes, on applique une méthode au résultat d’une méthode simplement en les mettant à la queue-leu-leu.

Par exemple, si je mets comptage dans la variable x il y aura cela dans x.
Si je mets comptage.title, il y aura cela :

>>> x = comptage.title().split(',')
>>> print x
['One', 'Two', 'Three', 'Four']

Dans la prochaine pythonnerie, les méthodes qui s’appliquent à des listes et des éléments de logique qui nous permettront de construire de vrais programmes…


Vidéo Fonctions et méthodes (2/2)

Fonctions et méthodes (2/2)

Transcript Vidéo

Faisons maintenant un tour rapide des méthodes qui s'appliquent aux listes.

Nous réaliserons un tout premier programme très simple qui montre comment se déroule l'exécution... et enfin nous verrons quelque chose d'absolument fondamental en informatique : la logique, et les tests sur la vérité de ce que l'on appelle une proposition logique (pas de panique, c'est plus simple que cela n'en a l'air), avec en prime un panorama sur quelques grands penseurs.

Vidéo

Fonctions et méthodes (2/2) Transcript

http://www.dailymotion.com/swf/video/xbk6xa_tech


Fonctions et méthodes (2/2) Transcript

Transcript

Vidéo Le contrôle de flux

Bonjour,

Nous avons vu les méthodes pour le texte, passons maintenant aux méthodes les plus importantes pour les listes avant de discuter logique.

Les méthodes des listes

Je vous ai expliqué comment on pouvait créer une liste qui contient des éléments, et comment on pouvait les modifier. Si l’on veut gérer des données de manière un peu dynamique, il faut aussi être capable d’enlever des éléments ou d’en rajouter.

Commençons par enlever un élément. Vous connaissez ces exercices du type « trouvez l’intrus » où un mot n’appartient pas à la même famille que les autres – par exemple :

mots = [‘pot’, ‘potier’, ‘potiron’, ‘potiche’]

Si je veux enlever le nom qui n’est pas de la même famille, ici ‘potiron’, une manière de procéder est d’abord de trouver sa position (rappelez vous les étages de l’ascenseur et le démarrage à 0), ici ce sera 2, et de dire que je remplace tous les éléments de 2 inclus à 3 exclu par un tableau vide :

>>> mots[2:3] = []
>>> print mots
['pot', 'potier', 'potiche']

Il y a une manière de procéder qui sera souvent plus élégante, qui est l’utilisation de la méthode remove(), qui signifie précisément enlever. Ce que l’on passe à remove, ce n’est pas une position dans le calcul de laquelle on peut toujours se tromper, mais tout simplement la valeur que l’on ne veut plus voir – au passage, je vais imprimer ce que retourne la méthode.

mots = ['pot', 'potier', 'potiron', 'potiche']
>>> print mots.remove('potiron')
None

‘None’ signifie en anglais « aucun ». C’est la manière de Python de vous dire ‘Circulez, y’a rien à voir ». En revanche, quand on affiche la liste, le travail est fait :

>>> print mots
['pot', 'potier', 'potiche']

C’est ce que je vous ai dit dans la pythonnerie précédente : si les fonctions retournent toujours une valeur, ce n’est pas toujours le cas des méthodes. Toutes les méthodes de texte retournent quelque chose, parce que le texte et les nombres ne sont jamais directement modifiés. Mais comme les listes, elles, sont directement touchées, il y a une modification définitive de leur état, et la méthode ne retourne rien.

C’est une autre méthode qui ne retourne rien que l’on utilise pour ajouter des éléments à une liste. C’est très simple, la méthode s’appelle ‘append()’, ce qui veut dire ‘rajouter’, et on lui passe ce que l’on veut rajouter.

Ajoutons le mot ‘poterie’ à notre liste de mots de la famille de ‘pot’ :

>>> mots.append('poterie')
>>> print mots
['pot', 'potier', 'potiche', 'poterie']
>>>

Nous savons maintenant enlever et rajouter des éléments. Une dernière méthode, qui encore une fois ne retourne rien, avant de passer à autre chose, c’est la méthode sort() qui trie la liste à laquelle on l’applique :

>>> mots.sort()
>>> print mots
['pot', 'poterie', 'potiche', 'potier']

Attention avant de vous enthousiasmer pour la méthode sort(), c’est un terrain miné. Remplaçons ‘poterie’ tout en minuscules par le même mot avec une majuscule :

>>> mots[1] = 'Poterie'
>>> print mots
['pot', 'Poterie', 'potiche', 'potier']

Et trions de nouveau :

>>> mots.sort()
>>> print mots
['Poterie', 'pot', 'potiche', 'potier']
>>>

L’ordre alphabétique n’est pas respecté. Pourquoi ? Parce que l’ordre du tri n’est pas, strictement, l’ordre « alphabétique », mais l’ordre des caractères dans le code ASCII dont j’ai déjà parlé. Vous pouvez le voir avec la table des caractères que l’on trouve dans le sous répertoire « système » du menu des accessoires dans Windows : on voit que toutes les majuscules passent avant les minuscules. C’est pour cela que les méthodes de texte comme upper, lower ou capitalize sont très importantes : elles permettent de tout ramener à une espèce d’unité commune et de rendre comparable le texte.

Les accents

Quand aux accents, ils sont perdus par là, en queue. Voilà en pratique ce que ça donne.
Prenons quelques noms relativement courants :

>>> noms = ['Bernard', 'Guérin', 'Guichard', 'Dupuis']
>>> print noms
['Bernard', 'Gu\xe9rin', 'Guichard', 'Dupuis']

L’accent apparait bizarrement (il est remplacé par un code). Quand on trie, c’est encore pire :

>>> noms.sort()
>>> print noms
['Bernard', 'Dupuis', 'Guichard', 'Gu\xe9rin']

Vous croyez que cela ira mieux si je mets la formule magique ‘coding’ ? Je mets tout dans un fichier.py, et je le lance, après affichage d’accents pour voir :

#-*- coding: cp1252 -*-
print 'éèàù'
noms = ['Bernard', 'Guérin', 'Guichard', 'Dupuis']
print noms
noms.sort()
print noms

>>> ============================ RESTART ============================
>>> 
éèàù
['Bernard', 'Gu\xe9rin', 'Guichard', 'Dupuis']
['Bernard', 'Dupuis', 'Guichard', 'Gu\xe9rin']
>>>

Les premiers accents passent bien, et pour celui dans la liste c’est le souk. Moralité : méfiance avec les accents.

Application des méthodes et fonctions

Nous avons vu un certain nombre de choses jusqu’à présent, et avant de passer à la suite je voudrais écrire ce que j’ose à peine appeler un programme pour mettre en œuvre au moins une partie des méthodes et fonctions. Ce programme va demander un nombre arbitraire de noms, les mettre dans une liste, et afficher le résultat trié.
Première chose, la formule magique qui va me permettre d’utiliser des caractères inconnus Outre-Atlantique., puis je crée une liste vide. Je fais cela uniquement pour que Python sache que le nom ‘liste’ fait référence à une liste, et ne râle pas si je lui applique des méthodes de listes. Je mets ensuite dans une variable compte un nombre de noms qui va être entré par l’utilisateur du programme. Je convertis en entier le texte tapé au clavier.

Ensuite, je crée une boucle en faisant prendre à une variable n les valeurs zéro à compte moins un. Dans la boucle, je lis une valeur – dans le texte je rajoute 1 à la variable n pour numéroter les noms, parce qu’il n’y a que dans les programmes et les ascenseurs que l’on numérote à partir de zéro – comme raw_input retourne du texte je peux lui appliquer une méthode pour normaliser et m’assurer que le tri ne cafouillera pas trop, puis je passe tout cela à la méthode append. En fin de boucle je trie, puis j’affiche.

Voyons ce que cela donne à l’exécution quand je lance le programme après l’avoir sauvegardé - Je vous le fais au ralenti.

# -*- coding: cp1252 -*-
liste = []
compte = int(raw_input(‘Combien de noms ?’))
for n in range(compte) :
    liste.append(raw_input('Nom n°' + str(n + 1) + ': ').capitalize())
liste.sort()
print liste

>>> ============================ RESTART ============================
>>> 
Combien de noms ? 4
Nom n°1: joe
Nom n°2: jack
Nom n°3: william
Nom n°4: averell
['Averell', 'Jack', 'Joe', 'William']
>>>

Les propositions logiques

Je voudrais maintenant passer à un sujet fondamental en informatique, celui des propositions logiques. Avec une expression comme ‘for <nom d’une variable> in <nom d’une liste>’ on fait prendre à la variable successivement toutes les valeurs de la liste. Il y a une autre manière d’utiliser ‘in’ suivi d’une liste, qui est de la précéder non pas d’une variable, mais d’une constante, comme ceci :

>>> 'Rantanplan' in liste

Et que nous dit Python ?

False

Qui signifie ‘faux’. Essayons autre chose :

>>> 'Averell' in liste
True

On obtient ‘True’, qui veut dire vrai.
En fait, ces expressions sont ce qu’on appelle des « propositions logiques » - on énonce un fait, qui est vrai ou qui est faux. Attention, l’égalité doit être stricte – si majuscules et minuscules ne collent pas, ou s’il y a des blancs d’un côté et pas de l’autre, on peut avoir des surprises :

>>> 'averell' in liste
False

On peut énoncer d’autres types de propositions logiques. On peut tester l’égalité entre deux valeurs. Comme ‘=’ est déjà utilisé pour l’affectation, c’est un double signe égal qui veut dire « est égal à » :

>>> x = 6
>>> x == 3 * 2
True
>>>

Différent peut s’écrire de deux manières au choix, != ou <> :

>>> x != 3*2
False
>>> x <> 3 * 2
False

On peut tester aussi avec >, <, >=, etc. Il faut faire très attention aux types de données, et tout ce que j’ai dit des problèmes d’accents et de majuscules avec les tris sont aussi des problèmes avec les comparaisons.
Il y a des cas où les comparaisons de types différents donnent le même résultat :

>>> 5 > 3
True
>>> '5' > '3'
True

Et d’autres pas :

>>> '5' > '31'
True
>>> 5 > 31
False

L’étude de la logique remonte au moins au précepteur d’Alexandre le Grand, Aristote. Les philosophes stoïques après lui étaient des logiciens redoutables. Quand l’Occident a sombré, les travaux d’Aristote ont été repris et developpés par des esprits de premier ordre comme vers l’an 1000 le Persan Ibn Sina, dont le nom a été Européanisé en Avicenne, ou au XIIème siècle Ibn Ruchd, né en Espagne et mort au Maroc, connu comme Averroes en Europe, dont l’œuvre passera aux grands penseurs médiévaux par des traducteurs juifs et dont l’influence fut telle qu’on le retrouve en statue sur la cathédrale de Chartres. Plus récemment, ce sont au XIXème siècle des gens comme les Britanniques Augustus de Morgan et surtout George Boole qui ont étudié la logique à fond. En informatique, George Boole a donné son nom aux valeurs « booléennes » qui peuvent valoir seulement vrai ou faux. Cela peut sembler simpliste, mais toute l’électronique, et par extension l’informatique, repose sur des composants qui réalisent les opérations étudiées par Boole – ‘vrai’ est remplacé par la présence d’une tension, ‘faux’ par l’absence de tension.

L’intérêt des propositions logiques, c’est que l’on peut les combiner avec trois opérateurs.
Le plus simple de ces opérateurs est not, qui transforme vrai en faux et réciproquement :

>>> x = 6
>>> x == 3 * 2
True
>>> not (x == 3 * 2)
False
>>> not (x < 1)
True
>>>

« Ou » (or en anglais) combine deux propositions logiques. La proposition logique résultant de la combinaison de deux propositions avec or est vraie dès que l’une au moins des deux propositions est vraie :

>>> (5 > 31) or ('Averell' in liste)
True

ET (and en anglais) n’est vrai que si les DEUX propositions logiques sont vraies :

>>> (5 > 31) and ('Averell' in liste)
False
>>> (5 > 3) and ('Averell' in liste)
True
>>>

Nous verrons dans la prochaine pythonnerie comment la logique nous permet de construire de vrais programmes.


Vidéo Le contrôle de flux

Le contrôle de flux

Transcript Vidéo

Après les variables simples, les listes et les opérations que l'on peut leur appliquer - ajouter ou enlever des éléments, en extraire des morceaux, parcourir tous les éléments … Et au passage, une différence fondamentale entre la modification d'une variable simple et celle d'une liste.

Vidéo

Le contrôle de flux Transcript

http://www.dailymotion.com/swf/video/xbk6ze_tech


Le contrôle de flux Transcript

Transcript

Vidéo Un vrai programme : un quiz

Bonjour,

Vous avez vu précédemment comment au lieu d'effectuer une suite d'opérations manuellement l'une après l'autre, vous pouviez les enregistrer dans un ficher .py et avoir le "moteur" python les exécuter les unes après les autres. Vous avez aussi vu "for" qui permet de répéter un certain nombre de fois des opérations dans ce qu'on appelle une boucle - Il suffit de décaler vers la droite les opérations dans le programme, python comprend qu'elles font partie de la boucle.

Les conditions

Plus récemment, je vous ai expliqué que python pouvait évaluer des expressions logiques, éventuellement inversées avec not ou combinées avec or ou and, et que l'on se retrouvait avec une proposition résultante qui était soit vraie, soit fausse. En fait, chaque proposition logique peut nous placer à une espèce de fourche dans le programme, avec une exécution conditionnelle d'opérations suivant que la proposition logique est vraie ou fausse.

Ceci est réalisé dans un programme python par une commande if suivie d'une proposition logique puis de deux points, plus un certain nombre d' opérations décalées vers la droite (nous avons déjà vu les deux points et le décalage avec for), puis d'autres opérations sans décalage. IF veut dire SI, et ce qui se passe c'est qu'à l'exécution python va évaluer la proposition logique; si elle est vraie, il va exécuter les opérations décalées (on dit "indentées") puis passer aux opérations au même niveau que la commande "if". Si la proposition est fausse, il va sauter la partie conditionnelle pour n'exécuter que les opérations au même niveau que le if.

On peut encore raffiner. Par exemple, on peut avoir un certain nombre d'opérations exécutées dans tous les cas, que j'appelle des opération communes. Ensuite on a une clause if, une proposition logique et deux points puis des opérations indentées qui ne sont exécutées que si la proposition logique est vraie. Ensuite, le mot "else" qui signifie sinon, suivi directement de deux points, puis un autre groupe d'opérations indentées qui ne seront exécutées que dans le cas contraire, c'est-à-dire si la proposition logique est fausse. Enfin, de nouveaux des opérations communes non indentées, qui sont exécutées indépendamment de la vérité de la proposition logique.

Encore plus fort. Avant le else on peut introduire un ou plusieurs blocs qui sont précédés par "elif", qui n'est pas un mot anglais mais du pur jargon informatique et la contraction de "else" et "if"; "elif" est en effet un mélange de ''else'' parce qu'on tombe dedans si la proposition logique précédente est fausse et uniquement dans ce cas là, mais aussi de "if" parce que l'exécution des opérations du bloc est liée à la vérité d'une proposition logique qui n'a pas forcément à voir avec la précédente.
Un exemple pourrait être le tarif d'entrée dans un musée "si vous avez plus de 65 ans, alors c'est moitié-prix, sinon si vous avez moins de 5 ans c'est gratuit, sinon si vous avez moins de 16 ans, alors c'est demi-tarif, sinon si vous êtes étudiant c'est demi-tarif, sinon c'est le tarif plein normal".

Une chose importante à remarquer c'est que dès que l'on tombe sur une condition qui est vraie, on ignore toutes les autres qui suivent. Si un enfant a moins de 5 ans, il a aussi moins de 16 ans mais dès qu'il a moins de 5 ans on ne se pose plus de questions.

Écrivons un petit exemple en python pour voir ce que cela donne en pratique. D'abord la formule magique pour les lettres accentuées, puis on va entrer au clavier un numérateur entier. Ensuite on va entrer un diviseur (je devrais plutôt dire dénominateur mais c'est long à taper et je suis fainéant), lui aussi entier. Et maintenant que va-t-on faire? La division, sauf que ça ne marche pas si le diviseur est 0. On va donc vérifier que le diviseur n'est pas zéro, puis afficher le résultat de la division. J'attire votre attention sur le fait que je transforme un des deux termes en décimal avec la fonction float() pour être certain d'avoir une division décimale et pas entière. Sinon, eh bien j'affiche simplement un message pour dire que ce n'est pas possible, puis j'affiche un message de fin.

Quand on exécute, on a la première question, à laquelle je réponds par exemple 45, puis la deuxième, disons neuf. Python évalue la proposition logique, 9 est différent de 0 donc la proposition est vraie et le résultat est affiché sous forme décimale, puis le message de fin.
On le refait? Allez, encore une fois…

Je rentre le numérateur, même valeur que tout à l'heure, puis le diviseur, et là j'entre 0. Quand Python évalue la proposition, ce coup-ci elle est fausse, il saute donc au bloc else et imprime le message de ce bloc avant d'imprimer le message commun.

Les boucles

Nous avons vu précédemment la boucle for, qui permet d'exécuter une opération un nombre prédeterminé de fois. Ce n'est pas toujours pratique. Par exemple, imaginez une bibliothécaire qui enregistre des livres qui viennent d'être rentrés. Elle ne va pas s'embêter à compter d'abord, puis dire « je vais traiter 11 livres ». Elle préfère prendre le premier livre de la pile, puis le suivant, puis dire "fini" quand elle a enregistré le dernier.

C'est ce que permet la boucle while (qui veut dire 'tant que‘) qui est comme if suivie d'une proposition logique.

If exécute des instructions une seule fois si la proposition est vraie . While exécute les instructions tant que la proposition est vraie, et arrête quand elle devient fausse. La boucle while demande deux choses :

Vous vous rappelez peut-être ce petit programme de la dernière pythonnerie où je rajoutais des noms à une liste dans une boucle for, après avoir dit "je veux entrer tant de noms".

Je vais le réécrire avec une boucle while. Je ne préciserai pas le nombre de noms, le programme comprendra seulement que ma liste est finie quand j'entrerai "stop". Le programme sera un peu plus compliqué, mais plus confortable à utiliser. Je crée ma liste vide comme avant. Ensuite, comme avant de rajouter un nom à la liste je dois le comparer à 'Stop', je vais devoir utiliser une variable que je testerai.

Pour entrer dans la boucle je lui affecte une valeur initiale qui peut être n'importe quoi tant que ce n'est pas 'stop'. Je mets par exemple "Python". Vous pourriez mettre votre nom, X, ou Louis XIV, ce serait pareil.

Je mets aussi 1 dans une variable n qui sert de compteur., puis j’ai ma boucle while avec la proposition logique « nom <> 'Stop' ». Vous remarquerez que Stop est en minuscules avec une majuscule, parce que comme avant je vais appliquer la méthode capitalize() à ce que j'entre. Si je mettais 'stop' tout en minuscules dans la proposition logique, j'aurais une boucle infinie parce que je ne pourrais jamais rendre la condition fausse. Dans la boucle, j'entre une valeur dans nom, avec la méthode capitalize(). Ensuite, je dois encore tester : si je rentre 'stop' je ne vais pas l'ajouter à la liste. J'ai donc une nouvelle condition et si ce n'est pas stop, j'ajoute à la liste et j'augmente n de 1 (on dit souvent 'j'incrémente n'). En fin de boucle, c'est comme avant, je trie et j'affiche.

Je vous montre ce que ça donne à l'exécution. Pour des raisons de place je ne vous montre que le code de la boucle while. J'ai donc initialisé 'nom' à 'Python' . Python évalue la proposition, elle est vraie. on entre donc dans la boucle. Je rentre un nom, j'appuie sur entrée, Python regarde si c'est différent de 'stop', c'est vrai, donc on ajoute à la liste et on incrémente n. On revient au début de la boucle, toujours vrai, autre nom, toujours pas stop, on ajoute, on incrémente, début de boucle toujours vrai, on entre un nouveau nom, toujours différent de stop, on ajoute, on incrémente, au début de la boucle c'est toujours vrai, on rentre un quatrième nom, et là je tape 'stop'. Là, la condition est fausse pour le if, donc on saute le bloc. On revient au début de la boucle, on teste la proposition et ce coup-ci elle est fausse. On sort donc de la boucle, on trie, et on affiche.

for, if elif else et while vous permettent de coder n'importe quel programme. En informatique il n'y a rien d'autre. Bon j'ai encore des choses à vous raconter, on va dire "quasiment". Mais comme vous le verrez prochainement, absolument tous les programmes reposent sur ces notions de base.


Vidéo Un vrai programme : un quiz

Un vrai programme : un quiz

Transcript Vidéo

Maintenant que nous maîtrisons les fondamentaux, un authentique programme de quiz, simple mais parfaitement fonctionnel, qui est en plus l'occasion de découvrir une des richesses de Python, les modules, et comment faire pour limiter les problèmes avec ces #$£% d'accents. :-°

Vidéo

Un vrai programme : un quiz Transcript

http://www.dailymotion.com/swf/video/xbk73y_tech


Un vrai programme : un quiz Transcript

Transcript

Vidéo Fichiers et exceptions

Bonjour,

Maintenant que nous avons (presque) toutes les billes en main, je vais vous montrer comment écrire un programme qui reste simple mais est un « vrai » programme, et pas simplement une démonstration un peu artificielle des possibilités du langage. Ce sera aussi l’occasion d’introduire quelques nouveautés importantes au passage.
Le programme que nous allons écrire est un quiz – c’est-à-dire qu’il va poser un certain nombre de questions, et qu’il va attendre de l’utilisateur une réponse, et vérifier si cette réponse est bonne.

Je vais constituer des couples avec une question, et la réponse associée. Peu importe la nature de la question et celle de la réponse – cela marchera aussi bien si la question est ‘Marignan ?’ et la réponse ‘1515’, ou si la question est ‘Les Fleurs du Mal ?’ et la réponse ‘Baudelaire’ ou ‘Roméo ?’ et ‘Juliette’. Dans mon exemple, la question sera le nom d’un pays de l’Union Européenne, et la réponse sa capitale. Mais vous pouvez adapter le programme aux sujets que vous souhaitez.

Quand on écrit un programme, la première chose à faire c’est d’imaginer quelqu’un en train d’utiliser le programme. Par exemple, va-t-on lui demander de répondre à un nombre fixé de questions, par exemple cinq, ou dix, ou lui envoie-t-on des questions, et des questions, et des questions, jusqu’à ce qu’il demande grâce et abandonne le programme ? Cela dépend un peu de l’utilisation que l’on veut en faire. Si le but est de tester les connaissances de l’utilisateur, ce sera plutôt un nombre de questions fixe – et pour python une boucle for. Si le but est de permettre à l’utilisateur d’apprendre à son rythme en jouant, il vaut mieux lui laisser le choix de continuer jusqu’à ce qu’il se sente au point, et lui laisser une chance de remonter son score s’il commence mal. En python ce sera plutôt un while.

C’est ce dernier cas que je vais retenir ; on va laisser à l’utilisateur le choix du moment où il s’arrête, et, comme dans la pythonnerie numéro 7, on le fera sortir de la boucle quand il entrera un mot particulier comme ‘stop’, ‘fin’ ou ‘bye’. Certains ont parfois un peu de mal à se rappeler quel mot utiliser. On peut se montrer conciliant, et offrir la possibilité d’utiliser plusieurs mots différents pour la même chose. Par exemple, je vais créer une liste qui contient tous les mots que je reconnais comme indiquant que l’utilisateur veut s’arrêter :

adieux = [‘fin’, ‘stop’, ‘bye’, ‘ciao’]

Vous pouvez allonger la liste autant que vous voulez, et rajouter ‘a+’, ‘auf wiedersehen’, ‘sayonara’, ‘aloha’ ou ‘kenavo’. Vous remarquerez que je mets tout en minuscules, parce que j’ai l’intention de convertir systématiquement en minuscules ce qu’entre l’utilisateur. Il faut faire attention d’être cohérent, sinon il ne pourra pas sortir de la boucle !
Ce que je vais faire, c’est entrer dans la boucle en mettant n’importe quoi qui n’est pas dans cette liste dans une variable que j’appelle reponse :
reponse = ‘X’

Un message d’instruction, et on tourne dans la boucle tant que la réponse n’est pas un mot d’adieux :

while reponse not in adieux :

Évidemment, pour sortir de la boucle, il faut lire la réponse, et la mettre en minuscules.

Jusque là, c’était simple. Maintenant il faut réfléchir un peu plus, parce qu’avant de lire une réponse je dois poser la question correspondante.

Il serait temps de se poser la question ‘Comment allons nous stocker les questions et leurs réponses dans le programme ?’. Il y a plusieurs solutions, y compris des solutions qui mettent en œuvre des choses que nous n’avons pas vues jusqu’à présent. Ce que je vous propose, et qui est relativement simple, c’est de stocker un couple (question, réponse) comme du texte, avec un caractère spécial, par exemple deux points pour les séparer. Grâce à la méthode split(), on peut découper le texte et récupérer la question séparément de la réponse.

Je vais mettre toute la série des questions/réponses dans une liste.
Maintenant, comment peut-on retrouver une question au hasard ? Il est temps pour moi de tirer un lapin de mon chapeau, mais avant de sortir le lapin je dois vous parler des modules.

Dans la toute première pythonnerie je vous ai expliqué que les langages de script tels que Python sont comme les couteaux suisses des informaticiens qui s’occupent du système. Ils peuvent servir à des tonnes de choses, mais pour ne pas surcharger le langage inutilement Python a été conçu de manière modulaire : c'est-à-dire qu’il y a un cœur du langage avec des choses utilisées par tout le monde, et quand l’on a besoin de choses d’usage moins général on branche un module dedans, c’est-à-dire que l’on charge explicitement des fonctions plus spécialisés. Pour accéder aux fonctions d’un module, il y a plusieurs manières de faire.

La première est d’utiliser tout au début du programme la commande import, de la manière suivante :

import <nom de module>

L’un des modules les plus classiques est le module qui s’appelle math. Tout le monde a besoin d’exécuter les opérations de base, et celles-ci font partie du cœur du langage. Les calculs de racine carrée, sans que ce soit des mathématiques de très haute volée, c’est déjà plus spécialisé. En gros, ce qu’il y a sur une calculatrice de base, vous le trouvez dans le cœur du langage. Pour ce que vous ne trouvez que sur une calculatrice scientifique, vous avez besoin de :

import math

La quantité de modules qui existent est énorme, et il faut se reporter à la partie de la documentation qui s’appelle ‘Standard Library’, ce qui veut dire ‘Bibliothèque Standard’, pour voir tout ce qui existe. Je ne mentionnerai que très peu de choses.

La fonction racine carrée s’appelle dans python, comme dans beaucoup de langages, sqrt qui est l’abréviation de « square root », dont racine carrée est la traduction exacte. Il y a juste un problème, c’est que même après avoir chargé le module on ne peut pas appeler la fonction directement (sous Idle, vous remarquerez qu’elle ne prend pas une couleur de fonction) :

>>> import math
>>> print sqrt(25)

Traceback (most recent call last):
  File "<pyshell#1>", line 1, in <module>
    print sqrt(25)
NameError: name 'sqrt' is not defined

Pour que cela marche, il faut préfixer le nom de la fonction par le nom du module.

>>> print math.sqrt(25)
5.0

En fait, il est possible de créer des synonymes pour les fonctions en affectant à une variable qui devient un peu spéciale.

>>> sqrt = math.sqrt
>>> print sqrt(16)
4.0
>>> racine_carree = math.sqrt
>>> print racine_carree(144)
12.0
>>>

Ce n’est pas parce que l’on peut faire quelque chose qu’il est bon de le faire, et je ne pense pas que prendre l’habitude de changer le nom des fonctions soit une bonne idée. Si vous vous habituez à toujours taper module.fonction avec le « vrai nom » de la fonction, cela vous aidera à vous rappeler, le jour où vous vous retrouverez tout seul à écrire un programme, d’une part quels modules vous avez besoin d’importer, et d’autre part quels sont les noms des fonctions. Je n’utiliserai donc pas de synonymes.

Il y a une autre manière de procéder si vous ne voulez pas préfixer chaque fois par le nom de module, qui est bien meilleure. Il faut importer de manière différente, comme cela - ‘from’, c'est-à-dire « depuis » le nom du module, « import » et les fonctions dont on a besoin, par exemple sqrt et quelques fonctions trigonométriques. Si vous vous préparez à une véritable orgie mathématique, vous pouvez mettre * qui veut dire « tout ».

>>> from math import *
>>> print sqrt(144)
12.0
>>>

Cette syntaxe est plus utilisée, d’ordinaire, quand on n’a besoin que de quelques fonctions du module. * peut être embêtant si l’on utilise plusieurs modules, parce qu’il n’est pas impossible que l’on ait deux fonctions différentes avec le même nom dans deux modules.

Le lapin que je vais sortir de mon chapeau est le module qui s’appelle random. « random » est en anglais un adjectif qui veut dire « au hasard », « aléatoire ». Les nombres tirés au hasard sont utilisés assez régulièrement en informatique, souvent pour tester des programmes, et parfois pour modéliser des phénomènes que l’on ne sait pas mettre en équations en physique, dans des calculs d’optimisation et même dans des calculs de risque, en assurance ou en finance par exemple. Random contient une fonction qui s’appelle choice (choix) qui prend comme argument une liste, et retourne un élément au hasard. Voilà ce que cela donne :

>>> liste = ['un', 'deux', 'trois', 'quatre']
>>> from random import choice
>>> print choice(liste)
quatre
>>> print choice(liste)
trois
>>> print choice(liste)
quatre
>>> print choice(liste)
deux
>>>

Vous voyez que la même valeur peut revenir plusieurs fois (particulièrement si l’on a peu de valeurs). Si notre but est de faire apprendre quelque chose, ce n’est pas forcément plus mal.

Tant que j’en suis dans les astuces, ce serait dommage de dire à quelqu’un qui tape à la question « Grece » la réponse « Athènes » - « C’est faux, c’est Athenes ». Surtout si l’utilisateur est plus grand et plus fort que vous. Il y a une méthode de texte dont je n’ai pas parlé jusqu’à présent qui s’appelle translate (traduit) et permet de convertir les lettres en fonction d’une table de conversion. Je vous montre comment cela marche sans m’apesantir. C’est une recette. Pour fabriquer la table de conversion, on a besoin dans le module string (je vous rappelle que ‘string’ fait référence aux chaînes de caractères et rien d’autre ..) d’une fonction qui s’appelle maketrans :

>>> from string import maketrans

Vous mettez dans une variable les lettres que vous voulez remplacer :

>>> accents="éèçêàôùû"

Vous mettez dans une autre variable ce qui les remplace ; les lettres doivent correspondre, c'est-à-dire que la première lettre de cette variable remplace la première de la précédente et ainsi de suite :

>>> ascii="eeceaouu"

Puis on appelle maketrans pour fabriquer la table de conversion à partir des deux variables précédentes.

>>> conversion = maketrans(accents, ascii)

Je l’utilise en mettant en minuscules, puis en appliquant translate :

>>> print 'Athènes'.lower().translate(conversion)
athenes
>>>

Vous êtes prêts ? Je vais maintenant mettre toutes les pièces ensemble.
Après la formule magique pour les accents, j’importe les deux fonctions dont j’ai besoin.
Ensuite je prépare mes constantes. D’abord la table de conversion pour éliminer les accents.
Puis ma liste de questions/réponses,
Puis les mots pour sortir de la boucle.
Puis j’affecte une valeur initiale à réponse qui me permet d’entrer dans la boucle.
J’affiche des instructions pour l’utilisateur.
Puis j’entre dans la boucle.
Je pioche une question au hasard.
Puis je lis une réponse, que je mets en minuscules, en posant comme question ce qui précède les deux points, c'est-à-dire le nom du pays.

Si la réponse ne me fait pas sortir de la boucle, j’élimine les lettres accentuées et je compare à la bonne réponse, c'est-à-dire ce qui suit les deux points dans ce que j’ai pioché. Si c’est bon je mets un smiley, si c’est faux j’affiche la bonne réponse en espérant qu’au bout de 5 ou 6 fois l’utilisateur saura écrire Ljubljana correctement,
Un message en sortie de boucle et c’est fini.

Voilà ce que cela donne.
Je vous laisse améliorer le programme et ajouter un compteur de bonnes réponses…


Vidéo Fichiers et exceptions

Fichiers et exceptions

Transcript Vidéo

Voyons comment améliorer l'exemple des liste vu précédemment de sorte que les couples questions/réponses se trouvent dans un fichier externe, facilement éditable. La manipulation des fichiers sera aussi l'occasion de découvrir les exceptions qui permettent de gérer les erreurs lors de l'exécution d'un script, ici, lors de l'ouverture d'un fichier.

Vidéo

Fichiers et exceptions Transcript

http://www.dailymotion.com/swf/video/xbsh58_tech


Fichiers et exceptions Transcript

Transcript

Vidéo Algorithmique

Bonjour,

Le programme que nous avons écrit dans la Pythonnerie n°8 est, comme annoncé, assez générique et peut se prêter à toutes sortes d’exercices du type question/réponse.

Quand on regarde dans le code ce qui est spécifique à un sujet donné, on voit que cela se ramène à deux choses : la liste qui contient les couples question/réponse, et l’espèce de consigne qui explique ce que l’on est censé répondre aux questions.
Je suis à peu près certain que si vous exécutez le programme devant n’importe qui, très rapidement arrivera la question « est-ce qu’on ne pourrait pas plutôt avoir des questions sur … » suivi du sujet qui tient particulièrement à cœur à votre interlocuteur.

Et là, que va-t-il se passer ? Si vous expliquez à la personne « c’est facile, tu rentres là les questions et les réponses et ici la consigne », environ 9 fois sur 10 vous allez voir la personne se dégonfler et battre en retraite en disant « Ouh là, moi l’informatique je n’y connais rien ».

Il faut dire qu’entrer la liste de questions et de réponses, avec les apostrophes, les virgules et tout n’est pas ce qu’il y a de plus passionnant à faire, surtout quand on a un assez grand nombre de questions. Ce serait beaucoup plus facile si l’on pouvait mettre les questions et réponses un couple par ligne, sans virgule et sans apostrophes.

Et tant qu’à faire, ce serait beaucoup mieux si d’autres que vous pouvaient entrer les données, dans le bloc-notes par exemple. Aucun problème avec Python qui comme tout langage de programmation qui se respecte, sait parfaitement manipuler des fichiers externes.

Dans un langage de programmation, c’est comme dans une application telle qu’un traitement de texte : on ouvre les fichiers, on les lit ou on les modifie, et on les ferme.

Il y a plusieurs manières d’ouvrir les fichiers avec Python, qui utilisent toutes la même fonction, je vous montre la manière recommandée, qui est de créer un bloc qui commence par with qui veut dire avec :

with open('nom de fichier', 'r') as f:
	Suite d’opérations

La fonction qui ouvre le fichier s’appelle open, qui justement veut dire « ouvre » . Elle prend deux arguments, le premier est le nom du fichier à ouvrir, et le deuxième dit ce que l’on veut en faire. Si l’on veut simplement lire des données, ce qui est notre cas, on mettra R, l’initiale de Read qui veut dire lire. Si l’on veut écrire un nouveau fichier, ou réécrire un fichier existant, on mettre W comme Write qui veut dire écrire. Si l’on veut rajouter des choses à la fin d’un fichier existant, ce sera A comme Append (et Ajoute). Si l’on veut à la fois lire et en plus réécrire dans le même fichier, ce sera R+. ‘As’ signifie en tant que, et f est le nom que je donne à un nouveau type d’objet – un fichier ouvert.

Encore une fois, je n’essaie pas de tout vous montrer mais de vous expliquer sur des cas simples comment les choses marchent. Lire un fichier composé de lignes est assez simple.

Tout d’abord, constituons notre fichier. Comme je vous l’ai dit, tout ce qui a besoin de changer, c’est la consigne et la série de questions et réponses. On va dire que notre fichier contiendra la consigne sur la toute première ligne, et les questions et réponses sur les lignes suivantes. Par exemple, nous allons dire quelque chose comme :
Quel est le prénom de cet auteur célèbre ?

Avec des questions et des réponses séparées par deux points sur les lignes qui suivent :

Hugo:Victor
Corneille:Pierre
Racine:Jean
Zola:Emile
Malraux:Andre
Vian:Boris
Queneau:Raymond
Camus:Albert
Verne:Jules

Voilà mon fichier, que je sauve sous le nom questions.txt.

Revenons à Python. Comme nous l’avons juste vu, je vais donc ouvrir mon fichier questions.txt avec la fonction open :

with open('questions.txt', 'r') as f:

f est un fichier ouvert, et tout comme le texte ou les listes, un fichier ouvert est associé à des méthodes. Celle qui nous intéresse tout particulièrement dans ce cas est la méthode readline(), qui ne prend aucun paramètre et retourne, sous forme de chaîne, la ligne qui est lue ; je vais mettre le résultat de la méthode dans la variable consigne :

consigne = f.readline()

Je ne vais pour l’instant lire que cela. J’affiche ce que j’ai lu. Il y a une méthode qui s’appelle close() pour fermer le fichier, mais avec with, je n’en ai pas besoin, sortir du bloc ferme automatiquement le fichier.

J’affiche un message quelconque derrière, par exemple ‘consigne lue’. Je sauvegarde mon fichier dans le même répertoire où j’ai déjà mis questions.txt, et je le lance.

Manifestement, ça marche. Mais je ne sais pas si vous remarquez quelque chose de bizarre :

La ligne blanche. Je n’ai pas demandé à Python d’imprimer une ligne blanche. En fait, il faut savoir que quand on passe à la ligne dans un fichier, on rentre un caractère invisible, le caractère « passage à la ligne ». Quand Python lit la ligne, il lit tout jusqu’à et y compris le caractère « passage à la ligne ». Quand il affiche la variable, il affiche aussi ce caractère. Comme après un print il passe à la ligne, on se retrouve d’abord avec le passage à la ligne lu dans le fichier, puis le passage à la ligne du print, et l’on se retrouve avec une ligne blanche.

Pour éviter cela, il y a notre vieille amie la méthode strip(). Quand on ne lui passe pas de paramètre, elle enlève les blancs. Mais on peut lui demander quel caractère enlever. Comme un passage à la ligne est un peu difficile à représenter, par convention dans plusieurs langages de programmation on le représente par deux caractères : un signe de division inversé (qu’on appelle couramment antislash), suivie d’un petit n.

Je relance mon programme, et là c’est bon. Au passage, quand on veut écrire dans un fichier (ouvert avec par exemple un paramètre w ou a) on utilise la méthode write(), qui ne retourne rien mais prend comme paramètre le texte que l’on veut écrire. Et là, il faut terminer la ligne par le signe de division inversé et le petit n.

Retournons à questions.txt. Je peux m’attaquer aux questions, toujours dans le bloc with où le fichier est ouvert. En fait, on peut imaginer que l’on a une espèce de pointeur qui se déplace. Quand on ouvre le fichier, il est au début, et à chaque fois que l’on lit une ligne, il avance. Après avoir lu la consigne, on se trouve donc prêt à lire la première question. Pour lire toute une série de valeurs dans un fichier, il y a quelque chose de très facile à faire. Vous vous rappelez peut-être comment, avec for, on peut mettre successivement dans une variable tous les éléments d’une liste ? On peut faire exactement la même chose avec un fichier :

Je fais donc créer une liste vide:

liste =[]
for ligne in f :
	liste.append(ligne.strip('\n'))

Et je boucle sur le fichier, rajoutant chaque ligne lue, dont j’élimine soigneusement le passage à la ligne, à ma liste. J’élimine aussi les blancs, parce que comme je dois comparer à ce que l’utilisateur entre je dois être certain que mes données sont bien propres.

Et voilà le résultat.

Si je n’avais pas enlevé le passage à la ligne, l’affichage de la liste aurait montré un antislash et un petit n à la fin de chaque élément. Vous pouvez essayer pour voir.

Modifier le programme de la Pythonnerie numéro 8 pour y intégrer ce que nous venons de voir est facile et vous n’avez pas besoin que je vous tienne la main pour le faire.

En revanche, si j’ai amélioré le programme en séparant les données de la logique, sur le plan pratique j’ai ajouté un risque d’erreur supplémentaire. Qu’est-ce qui va se passer ? Imaginez que le programme plaise et que les gens commencent à se le passer de clef USB à clef USB ou se l’envoient par mail, il va bien arriver un moment où quelqu’un qui n’aura pas très bien compris comment cela marche enverra le fichier .py en oubliant le fichier de données associé. Autrement dit, l’absence du fichier de données est une éventualité à prendre en compte dans le programme. Et si je lance le programme sans le fichier attendu (je l’ai renommé) ce que l’on obtient est assez effrayant pour le non-initié :

>>> ======================= RESTART =========================
>>> 

Traceback (most recent call last):
  File "mon_programme.py", line 7, in <module>
    with open('questions.txt', 'r') as f:
IOError: [Errno 2] No such file or directory: 'questions.txt'
>>>

La question est : peut-on rendre le message d’erreur à la fois moins barbare et plus explicite pour quelqu’un qui ne connait rien à Python ? La réponse est oui, vous auriez été surpris du contraire.

Quand on lance une opération qui a des risques d’échouer, on la met dans un bloc qui commence par try (qui signifie « essaie ») comme ceci :

try:
	with open('questions.txt', 'r') as f:
		consigne = f.readline().strip('\n')
		questions =[]
		for ligne in f:
			questions.append(ligne.strip('\n'))

Ce bloc va être suivi de deux blocs.

Le premier bloc commence par except, qui est l’abréviation d’exception, qui est un mot plus politiquement correct pour dire erreur, puis le type d’erreur auquel on s’attend. Dans le message d’erreur, c’est le premier mot de la ligne : IOError (IO, qui se lit aille-o, est l’abréviation de Input/Output, entrée/sortie, qui décrite entre autres les opérations de lecture et d’écriture dans les fichiers).

On va donc intercepter les erreurs de type IOError et afficher un message moins effrayant que le message standard Python :

except IOError:
	print 'Ce programme nécessite le fichier questions.txt'

Le deuxième bloc est un bloc qui commence par else :, comme avec if, et va être suivi de ce que l’on exécute lorsque que l’on n’attrape pas au vol une erreur de type IO – à savoir le reste du programme. Si l’on relance le programme et que le fichier questions.txt est absent, voilà ce que cela donne :

>>> ======================= RESTART =========================
>>> 
Ce programme nécessite le fichier questions.txt
>>>

C’est beaucoup plus sympathique que Python livré à lui-même. Bien entendu, quand le fichier est là tout fonctionne comme à l’ordinaire.


Vidéo Algorithmique

Algorithmique

Transcript Vidéo

Dans ce chapitre plus théorique, mais aussi historique, découvrons ce qu'est l'algorithmique, une méthode de raisonnement fondamentale pour toute personne qui se lance dans la programmation, ou tout simplement la résolution de problèmes, comme par exemple la création d'une fonction de simplification de fractions.

Vidéo

Algorithmique Transcript

http://www.dailymotion.com/swf/video/xbznq8_tech


Algorithmique Transcript

Transcript

Vidéo Créer une fonction

Bonjour,

Nous commençons à avoir vu pas mal de choses et il est temps de parler d'un sujet très important en informatique : l’algorithmique.

Disons qu'un jour vous êtes abîmé dans la méditation quand surgit un de vos amis. Gentil, fidèle, mais le genre un peu trop enthousiaste. Il débarque en disant « j'ai eu une super idée – on devrait écrire un programme qui réduit les fractions ! »

J'ai pas mal pensé au truc, voilà comment on peut faire : on demande à l'utilisateur de rentrer une fraction comme du texte, sous la forme numérateur/barre de division/dénominateur.

Derrière, un coup de split() nous permet d'obtenir un tableau avec le numérateur dans la case 0 et le dénominateur dans la case 1. On vérifie au passage que l'on a bien les deux éléments attendus.

Après, un peu de strip() pour éliminer les blancs, et on utilise try pour convertir en entier en attrapant les erreurs au passage au cas où quelqu'un aurait fait des fautes de frappe ou entré des lettres.

On vérifie aussi s'il n'y a pas un gros malin qui a mis 0 au numérateur ou au dénominateur, ou 1 au dénominateur.

Après, ça devient intéressant. Comment est-ce qu'on ferait à la main pour réduire ? Bon, la méthode classique c'est de décomposer numérateur et dénominateur en un produit de nombres premiers, puis de virer tout ce que l'on retrouve en haut et en bas. Après il n'y a plus qu'à re-multiplier. Je pense qu'on pourrait faire quelque chose de similaire avec python, voilà comment.

Suppose que je transforme et le numérateur et le dénominateur en une liste de leurs facteurs, par ordre croissant. Au début, je mets 1 dans une variable n qui sera le numérateur réduit, et 1 dans une variable D qui sera le dénominateur réduit. Bon. Je me prends deux indices, un pour me déplacer dans la liste du numérateur, l'autre dans celle du dénominateur, et je commence en position 0 pour les deux.

Quand les deux facteurs sont égaux, c'est quelque chose qu'à la main j'ai rayé. J'ignore et je passe aux suivants. Même chose. Quand ils sont différents, comme tout est trié, je sais que je ne peux pas retrouver le plus petit des deux dans l'autre liste ; il va forcément rester. Donc, là, je multiplie le dénominateur par 2 et j'avance uniquement l'indice du bas. Deux nombres égaux, j'avance mes deux indices, là j'ai 3 plus petit que 11, donc je multiplie n par 3 et j'avance seulement l'indice du haut, toujours plus petit, même truc, après c'est 11 qui est plus petit que 29, je multiplie et j'avance, 17 pareil, deux nombres égaux je les ignore et comme je suis au bout des deux listes je m'arrête. Si une liste n'était pas finie, je n'aurais qu'à multiplier tout ce qui reste.

Et tu remarqueras que je me retrouve avec les mêmes produits qu'à la main.

Ça n'est pas super, comme truc ?

Et, là, vous répondez : « Hmm, oui. Super intéressant. »

En fait, ce que votre ami vient de vous expliquer, c'est ce qu'on appelle un algorithme, c'est-à-dire la résolution méthodique et générique, parce que cela marche avec n'importe quelle fraction, que l'on traite en informatique avec les if , while et for que l'on retrouve dans tous les langages.

A l'époque du Calife Haroun Al Rashid, quand Charlemagne était empereur en Occident, vivait dans Baghdad la Magnifique un savant persan nommé Al Khawarismi. Le génie d'Al Khawarismi n'était pas dans une lampe, il était dans sa tête. Isaac Newton a dit, pour rendre hommage à ses prédécesseurs, que s'il avait vu plus loin que les autres, c'est qu'il était monté sur les épaules de géants. Al Khawarismi a fait pareil. Plutôt que de chercher à devenir calife à la place du calife ou que de draguer les princesses sur son tapis volant, il s'est intéressé à la géographie de Ptolémée qu'il a complétée et corrigée, il a inventé des instruments astronomiques, et surtout il s'est penché sur des choses fort intéressantes qui s'étaient passées par là quelques siècles plus tôt. Vers 500, Aryabatha avait inventé quelque chose que Pythagore, Thalès, Euclide, Archimède et tous les grands savants grecs ne connaissaient pas, et vers 630 Brahmagupta avait développé ces travaux. Cette grande invention, c'est cela : le zéro, que les Arabes ont appelé « sifr », qui signifie « vide », mot qui a donné « chiffre » en français. Outre le zéro, les Indiens avaient tout un système de symboles pour représenter les nombres dont l'intérêt n'a pas échappé à Al Khawarismi, ni à Al Kindi, autre savant de Baghdad qui lui venait d'Arabie. Quand l'occident calculait comme ceci, l'orient calculait comme cela. En fait, Al Khawarismi et Al Kindi n'utilisaient pas exactement ces symboles, qui ont été inventés un poil plus tard par des mathématiciens du Maghreb. Mais c'était très proche. Évidemment, il n'y avait pas photo. Il est vrai, qu'à l'époque, les vikings commençaient à déferler avec régularité sur les côtes d'Europe occidentale, où l'on avait d'autres soucis que la comptabilité créative.

Pour l'anecdote, un moine d'Auvergne de la fin du 10ème siècle, Gerbert d'Aurillac, découvrit les chiffres dits « arabes », les arabes disent « indiens », en étudiant en Espagne, et c'est lui qui les introduisit en Europe. On raconte qu'il faisait le mur du couvent pour aller étudier avec des professeurs arabes. Gerbert était très en avance sur son temps, ce qui ne l'a pas empêché de devenir pape sous le nom de Sylvestre II. Mais les chiffres arabes ne s'imposèrent pas vite, et on les voit en concurrence avec le boulier sur cette gravure du tout début du 16ème siècle.

Revenons à Al-Khawarismi. Armé des découvertes indiennes, il s'est attaqué avec méthode à la résolution des équations du premier et du deuxième degré. En particulier, il a réalisé qu'en ajoutant la même quantité négative des deux côtés d'une équation, on ne changeait pas le résultat et l'on éliminait un côté. Il a appelé cette opération « restauration », qui se dit « al-jabr » en arabe, et qui a donné « algèbre ».

Le nom d'al-Khawarismi est devenu algorizmi dans les traductions latines de ses œuvre, comme ce manuscrit du 12ème siècle conservé à Cambridge. Dans l'écriture manuscrite de l'époque, z et t étaient faciles à confondre, et algorizmi est devenu algoritmi ; l'un des ouvrages est devenu algoritmi de numero indorum « Al-Khawarismi sur les chiffres indiens » et voilà d'où vient algorithme. Ne me demandez pas d'où sort le h, mais en tout cas il n'y a pas de Y – parce que cela ne vient PAS du grec. Al Khawarismi a réellement été le premier à appliquer des méthodes de résolution systématiques.

L'algorithmique n'est pas une matière simple. On pourrait procéder autrement que suggéré ici pour réduire des fractions, par exemple commencer par calculer le Plus Grand Diviseur Commun. Après il faut voir le nombre d'opérations nécessaires et la complexité de ces opérations pour comparer les algorithmes et voir lequel permet d'obtenir le résultat le plus vite. Ce sont des questions très importantes en pratique ; par exemple, les gens qui font du film d'animation, ou les gens qui programment des jeux, ont des algorithmes complexes de traitement des images, d'animation et autres. Pour eux, avoir des algorithmes efficaces qui vont vite même sur des machines moyennement puissantes est vital.

Revenons à notre ami qui nous dit : ‘Je suis content que ça te plaise. Dis, tu crois que tu pourrais écrire un truc pour décomposer un nombre en une liste de facteurs ? C'est le seul truc qui me manque.’

Bon, les amis, c'est quand même sacré. On lui fera ça dans la prochaine pythonnerie.


Vidéo Créer une fonction

Créer une fonction

Transcript Vidéo

Dans la continuité du chapitre sur l'algorithmie, voyons comment créer nos propres fonctions. Nous allons écrire des morceaux de code que nous pourrons réutiliser par la suite, pour ne pas avoir à réinventer la roue à chaque fois.
Les fonctions sont une composante essentielle des langages de programmation, et Python ne fait pas exception.

Vidéo

Créer une fonction Transcript

http://www.dailymotion.com/swf/video/xccg9l_tech


Créer une fonction Transcript

Transcript

Vidéo Coder comme un pro

Bonjour,

Je vous ai dit dans la pythonnerie n°10 que nous ferions quelque chose pour décomposer un nombre entier en une liste ordonnée de ses facteurs. Ce quelque chose, c'est une fonction. Nous avons déjà rencontré plusieurs des fonctions de python, et nous en avons même rencontré une, range() qui retourne une liste. On peut enrichir les langages de programmation en rajoutant nos propres fonctions à ce qui existe déjà.

Qu'est-ce qu'on va mettre en général dans une fonction ? Typiquement des calculs dont on sait que l'on va être amené à les exécuter plusieurs fois, mais appliqués à des valeurs différentes. Si l'on cherche à réduire les fractions de la manière expliquée dans la pythonnerie n°10, nous avons besoin de décomposer et le numérateur, et le dénominateur. En plus de cela, on peut imaginer utiliser la décomposition d'un nombre en ses facteurs premiers pour d'autres choses, des calculs de PGCD par exemple. La décomposition en facteurs est donc un bon candidat pour créer une fonction.

On crée une fonction en tapant « def », comme définit, suivi d'un nom et de paramètres entre parenthèses, puis les deux points et le bloc décalé habituel. Pour l'instant ne créons qu'un squelette, que l'on va habiller plus tard. On crée une liste, pour l'instant on se borne à y mettre le paramètre, et l'on met le mot-clef « return » suivi de ce que la fonction renvoie, à savoir la liste :

def decompose(n):
	facteurs = []

	resultat.append(n)
	return facteurs

Le corps du programme, lui, va lire un entier, l'imprimer, puis imprimer le résultat de la fonction :

val = int(raw_input("Entrez une valeur : "))
print val
print decompose(val)

Quand on exécute, Python « saute » toutes les définitions de fonction (j'en ai créé une mais il pourrait y en avoir plusieurs) pour commencer à la première instruction qui n'est pas dans une fonction. Quand il rencontre le nom de la fonction, on dit l'appel à la fonction, il mémorise là où il en était dans le programme, passe la ou les valeurs valeur qu'on lui donne à la fonction qui les récupère dans des variables qui n'ont en général pas le même nom, va exécuter le code de la fonction, puis retourne la valeur calculée là où il en était.

Il y a une chose importante avec une fonction, c'est qu'elle est complètement déconnectée du reste, que l'on appelle le programme principal : si vous avez dans le programme principal et dans la fonction des variables qui portent le même nom, par exemple n, il n'y aura aucune confusion. Les noms de variable dans une fonction ou le programme principal, c'est un peu comme les prénoms dans une famille : à l'intérieur de la famille, il faut des prénoms distincts. En revanche, aucun souci à avoir des gens qui portent le même prénom mais pas le même nom de famille, il n'y a aucune confusion possible. Avec les fonctions, c'est pareil. La fonction, c'est la famille.

Il n'y a que deux points de contact entre une fonction et l'endroit où on l'appelle (qui peut être du code à l'intérieur d'une autre fonction) : les paramètres, qui est ce que l'appelant fournit en entrée à la fonction, et le résultat retourné par la fonction ; en fait, il y a encore un autre moyen de communiquer que nous allons voit tout à l'heure, mais c'est un peu spécial. Quand on définit la fonction, on donne un nom a priori aux paramètres, qui est le nom sous lequel on va les utiliser dans la fonction. Quand on appelle la fonction, il n'y a nul besoin qu'on lui passe une variable du même nom ; en fait, en général on n'a même pas besoin de passer une variable, on peut très bien passer une valeur constante.

Du côté du résultat retourné par la fonction, c'est exactement la même chose : la fonction va mettre le résultat calculé dans une variable, et retourner cette variable. Qu'on affecte ce résultat à une variable qui a le même nom ou pas que dans la fonction n'a aucune importance, et si l'on veut simplement afficher le résultat, ou le transmettre en paramètre à une autre fonction, on n'a aucun besoin de l'affecter à une variable dans la partie appelante.

Nous l'avons déjà vu, y compris d'ailleurs avec les méthodes, on peut aussi bien écrire

>>> nom = 'Python'
>>> print len(nom)
6
>>> print len('Pythonneries')
12
>>>

On n'a aucune idée du nom que prend le paramètre dans la fonction, et l'on s'en bat l'œil.

Avant de passer à l'étude de notre fonction à proprement parler, j'ai une chose importante à vous montrer. Sauvegardons notre squelette de fonction, et lui tout seul, sous le nom mes_fonctions.py – je sais bien qu'il n'y en a qu'une, mais je suis de nature optimiste.

Maintenant, reprenons notre programme, mais au lieu d'avoir tout le code de la fonction remplaçons le par

from mes_fonctions import decompose

J'espère que cela vous rappelle quelque chose – c'est exactement la syntaxe que nous avons utilisée pour rajouter des modules à Python.

Relançons le programme modifié, et ô joie cela fonctionne comme avant :

J'espère que vous mesurez tout l'intérêt de la chose : le code que vous écrivez dans des fonctions, c'est du code réutilisable dans autant de programmes que vous le voulez, des programmes écrits par vous ou par d'autres.

Il suffit de mettre à près from le nom du fichier .py qui contient les fonctions, après import la ou les fonctions qui y sont définies et que vous voulez utiliser, et hop.

Il est maintenant temps d'étoffer notre fonction, parce qu'elle ne fait pas grand-chose... Comment faisons-nous d'habitude pour décomposer un nombre ? Tout d'abord, on regarde s'il est pair – c'est le cas, on va donc le diviser par 2 :

343

Là, ce n'est pas évident. Manifestement pas divisible par 5, 3 + 4 + 3 font 10 donc il n'est pas divisible par 3 – je vous souffle la réponse, 343 est divisible par 7

49

Là on retombe en terrain connu, on divise par 7, et il nous reste un 7 que l'on rajoute évidemment à la liste des facteurs.

Manifestement, on va avoir besoin d'une liste de nombres premiers, parce que le programme ne peut pas les inventer.

premiers = [2, 3, 5, 7, 11, 13, 17, 19, 23]

Nous, les humains, on a des trucs pour savoir si un nombre est divisible par 2, par 5 ou par 3. Un ordinateur, ça manque de subtilité et ça ne fait pas beaucoup de différence entre 3 et 317 (qui est premier). La bonne manière en programmation de voir si un nombre est divisible par un autre, c'est de lui appliquer l'opération que l'on appelle « modulo » et qui est le reste de la division entière. Il y a un opérateur spécial pour cela en python, qui est représenté par le signe pourcent. Si j'écris nombre plus grand % nombre plus petit, si le nombre plus grand est divisible par le nombre plus petit j'obtiens 0, sinon j'obtiens le reste de la division, par exemple :

>>> print 25 % 5
0
>>> print 300 % 5
0
>>> print 12 % 5
2
>>>

Pour voir si un nombre est divisible par un nombre premier, il suffit donc de voir si nombre % nombre premier donne zéro ou pas.

Écrivons donc notre programme. Je commence par une liste de nombre premiers. Elle est très courte, c'est exprès. Ensuite, je commence en position 0 de la liste des nombres premiers, c'est-à-dire avec la valeur 2

i = 0

Je vais prendre successivement mes valeurs dans la liste des nombres premiers, et regarder si mon nombre est divisible :

if (n % premiers[i] == 0) :
	# Dans ce cas là je le divise :
	n = n / premiers[i]

Et je rajoute mon nombre premier à la liste des facteurs

facteurs.append(premiers[i])

Je ne touche pas à i parce qu'il est possible que notre nombre soit divisible plusieurs fois par le nombre premier.

else:
	# Sinon j'essaie le nombre premier suivant en augmentant de 1 ce que j'ai dans la boîte i :
	i = i + 1

La grosse question, quand on a une boucle, c'est comment en sort-on ? Est-ce que je vais aller jusqu'au bout de ma liste de nombres premiers ? Ce serait un peu idiot si le nombre en entier est par exemple 2 ou 3. En fait, comme je divise n au fur et à mesure, il suffit que je continue à essayer tant que la valeur dans ma boîte n est supérieure ou égale au nombre premier que j'essaie – si la valeur de n est plus petite que le nombre premier, le reste de la division entière sera forcément n et ne pourra pas être zéro.

Et ensuite, je retourne ma liste.

Je vais modifier un tout petit peu mon programme principal, pour pouvoir entrer des valeurs jusqu'à un zéro qui signifiera la fin de ma série de nombres à décomposer.

Testons un peu - 24
Apparemment ça marche bien
25
Impeccable
26
Boum.

Regardons de plus près ce qui se passe dans le programme.

N vaut 26. Je commence avec i = 0, donc le nombre premier 2.
26 est plus grand que 2, j'entre dans la boucle.

26 est divisible par 2, le modulo donne 0, je divise n par deux pour obtenir 13, et je rajoute 2 à mes facteurs. 13 est plus grand que 2, donc je continue, en revanche il n'est pas divisible par 2, et j'incrémente i de 1 pour tester 3.

Vous voyez ce qui va se passer : comme 13 est premier, il n'est divisible par aucun des nombres premiers de la liste et l'on fini par « sortir de la liste ».

Qu'est-ce que l'on fait dans un cas comme cela ? On a 3 options.

La première option c'est ne rien faire. On se dit : après tout, le programme qui appelle n'a qu'à attraper l'exception et faire ce qu'il faut.

La deuxième option c'est de se dire : il n'y à qu'à allonger la liste des nombres premiers. Si 13 en avait fait partie, on n'aurait pas eu de problème. Si l'on met par exemple une liste de 100 nombres premiers, plus de soucis.

Il y a des tas de gens qui réagissent comme cela. Utiliser une liste plus longue est certainement une bonne idée. Mais chercher à résoudre un problème simplement en utilisant une liste plus longue, ce n'est pas résoudre le problème, c'est le dissimuler – c'est exactement la même chose que de balayer la poussière sous le tapis. Un jour, on aura probablement un nombre qui nous fera sortir de la liste, aussi longue soit-elle.

La troisième option, c'est de chercher à comprendre mieux et résoudre le plus intelligemment possible – ce que nous ferons dans la prochaine pythonnerie.


Vidéo Coder comme un pro

Coder comme un pro

Transcript Vidéo

Essayons maintenant de réfléchir en détail au comportement de nos fonctions. Nous allons donc mettre à mal nos fonctions et voir comment notre programme réagit, puis nous ferons en sorte de corriger les problèmes qui surviendront.
Ce chapitre met l'accent sur la conception : c'est une étape de réflexion indispensable pour réaliser des programmes qui se comportent correctement en toutes circonstances.

Vidéo

Coder comme un pro Transcript

http://www.dailymotion.com/swf/video/xccge5_tech


Coder comme un pro Transcript

Transcript

Vidéo Récursion (1/2)

Bonjour,

Nous en sommes restés à une fonction de décomposition de nombres en facteurs, qui assez fâcheusement balance un message peu plaisant.

Il y a quelque chose d'assez gênant dans le message d'erreur : c'est qu'il fait référence à des spécificités de l'algorithme. Que voulez-vous qu'y comprenne un utilisateur, ou même un autre programmeur qui utilise la fonction sans en avoir regardé le code ? Cela n'indique en rien la raison réelle de l'erreur. Et d'abord, si l'on a une erreur parce que l'on tombe sur un nombre premier non répertorié, ne pourrait-on pas simplement l'ajouter à la liste ?
Essayons donc d'attraper l'exception au vol.

Je vais déplacer le bloc du while, et le mettre dans un bloc try. Si je déborde de ma liste, et bien je vais dire que je suis tombé sur un nombre premier que je ne connaissais pas.

Hmm, est-ce que c'est toujours vrai cette histoire là ? Avec une liste de nombre premiers qui va jusqu'à 11, c'est vrai jusqu'à 169. Pourquoi 169 ? Parce que 169 n'est divisible par aucun des nombres premiers entre 2 et 11, mais n'est pas premier. 169, c'est 13 fois 13 ; comme 13 est le plus petit nombre premier qui n'est pas dans notre liste, 13 fois 13 est le plus petit nombre qui nous causera une exception sans être premier. Si la valeur de n que l'on a quand on rencontre l'exception est plus petite que 169, alors n est forcément premier. Si n vaut 169 ou plus, il est peut-être premier, il n'est peut-être pas premier, on ne peut pas dire.

En règle générale, rappelez-vous que dans la liste premiers [-1] représente le dernier élément –11 dans notre exemple. La limite où l'on sait dire premier/pas premier est le carré du plus petit nombre premier après celui-là – que l'on ne connaît pas. On sait que ce n'est pas ce nombre plus 1, qui est forcément pair. On peut prendre pour limite notre dernier élément plus 2, au carré. Ce nombre ne sera pas forcément premier, mais on est certain qu'on ne dira pas de bêtise en dessous.

Et que va-t-on faire si le nombre que l'on traite est plus grand ? On va botter en touche, en émettant une exception – parce qu'on peut créer des exceptions, ce que l'on fait avec le mot-clef raise qui signifie pas mal de choses en anglais, de l'augmentation salariale à la levée d'une armée en passant par les cheveux qui se dressent sur la tête. Nous retiendrons « lever » en français, et vous entendrez souvent dire que l'on « lève une exception ».

Le plus simple est de vous montrer directement comment cela se fait...

On traite donc le cas où l'on déborde de la liste. Si à ce moment là n est plus petit que la limite que nous avons vue, pas de problème, il est premier, on l'ajoute. Sinon, plutôt que de dire des bêtises, on va lever une exception en utilisant raise suivi d'une espèce de fonction qui correspond au type d'exception que l'on veut, dans un cas comme cela ValueError s'impose, avec comme paramètre le message d'erreur. On va dire « nombre trop grand », ce qui n'est pas strictement exact parce qu'il y a des nombres plus grands que l'on factoriserait sans problème, mais bon, au moins cela mettra l'utilisateur sur la piste de ce qui ne va pas.

Testons tout de suite – 25 évidemment pas de problème, 26 déclenche IndexError mais comme 13 est plus petit que 169 on est certain qu'il est premier, 168 passe bien, et 169 se plante, MAIS avec un message beaucoup plus informatif qu'avant. En fait, une fonction a deux manières de retourner des informations au programme appelant : ce qu'elle retourne en cas normal, et les exceptions quand elle se retrouve dans un cas anormal.

Etes-vous contents de la fonction maintenant ? Moi pas. Il y a encore plein de choses qui ne vont pas, et que je vous laisserai corriger. Par exemple, si l'on appelle decompose(1), on a une liste vide. Est-ce que c'est bien ? Pas forcément. On pourrait dire que la décomposition de 1, c'est la liste qui contient 1. Mathématiquement hasardeux, mais si après on veut réduire des fractions cela peut tenir debout.

Decompose(0) donne aussi une liste vide. Qu'est-ce qu'on fait ? Dans le cas de 0, je serais plutôt partisan de l'exception ValueError, mais cela se discute.

Decompose(-4) donne une liste vide. Pour les nombres négatifs, on pourrait commencer par mettre -1 dans la liste, puis les multiplier par -1 et les factoriser normalement. Ou alors une exception, au choix.

Si le nombre est décimal et pas entier, encore une liste vide. Dans ce cas, je serais plutôt partisan de comparer n à int(n), et lever une exception s'ils sont différents.

A partir du moment où une fonction peut être utilisée par plein de programmes différents, il faut complètement la bétonner et être très défensif, et essayer de prévoir tous les cas bizarres qui peuvent se produire. Ce n'est pas toujours facile.

Un collègue israélien qui avait travaillé dans l'aéronautique m'a raconté qu'une fois en plein vol un avion de chasse avait vu certains systèmes de bord se planter complètement. Le pilote est parvenu à rentrer à la base, ils ont cherché, et ils ont compris.
Au moment du problème, l'avion était au dessus de la Mer Morte. Il faut savoir que la Mer Morte est dans une dépression, et qu'elle se trouve 422 mètres au dessous du niveau de la mer. 422 mètres, c'est largement assez pour faire passer un avion de chasse. Au moment du problème, l'avion volait à une altitude négative, un cas qui n'avait pas été prévu.

Le gros problème en informatique, ce n'est pas en général de trouver un algorithme pour le traitement, c'est de définir le domaine d'application de la fonction, de définir le plus nettement possible la limite qui dit « là cela va fonctionner, là on ne sait pas traiter », et de donner à la fonction le comportement le plus logique possible quand on ne sait pas traiter.

Il y a autre chose qui n'est pas très bon dans la fonction. On y code la liste des nombres premiers, qui potentiellement peut être très longue. A chaque appel de la fonction, l'ordinateur devra se reconstruire la liste, toute constante qu'elle soit. Dans une boucle, cela peut demander proportionnellement beaucoup de travail, qui est complètement inutile. Pas terrible.

Ce que je vous suggérerais de faire, c'est d'éliminer la liste de la fonction, et de l'ajouter à la liste des paramètres. Le programme appelant la remplira une fois, puis ne passera, si vous vous rappelez comment python gère ses listes, que la valeur qui indique où est le début de la liste en mémoire. En prime, le programme pourra passer une liste de la taille qui correspond à ses besoins.

Finalement, si vous voulez faire du travail de pro, il faut documenter votre fonction dans des commentaires. Vous allez expliquer ce que vous attendez, avec éventuellement les cas particuliers, dire ce que vous retournez, les exceptions que vous pouvez déclencher, signer et mettre la date. Et si un jour quelqu'un améliore la fonction, il ou elle dira les modifications apportées, signera, et datera. C'est comme cela que les professionnels travaillent.

Merci pour votre attention.


Vidéo Récursion (1/2)

Récursion (1/2)

Transcript Vidéo

Une fonction qui s'appelle elle-même ? L'idée peut paraître folle, et pourtant c'est le principe de la récursion que nous allons découvrir dans ce chapitre. Le but est de produire un code capable de s'appeler lui-même, pour réussir à exécuter de nombreuses opérations en quelques lignes de code seulement.

Vidéo

Récursion (1/2) Transcript

http://www.dailymotion.com/swf/video/xdu50s_tech


Récursion (1/2) Transcript

Transcript

Vidéo Récursion (2/2) : trier

Bonjour,

Je voudrais maintenant vous présenter une manière de programmer qui n'est pas intuitive mais très intéressante. Avant cela, il faut que je vous parle un peu de sites web.

Je vous ai dit dans la toute première Pythonnerie que Python faisait partie des langages utilisés pour programmer les sites web. Il y a par exemple un gestionnaire de contenu, c'est-à-dire, en gros, un programme pour gérer un site web, qui est très connu, Django, et qui est entièrement développé en Python. A quoi sert un langage de programmation comme Python sur un site web ? Essentiellement à créer dynamiquement ce que l'on appelle des pages HTML.

Ce n'est pas mon but de vous parler en détail de programmation web. Mais si vous voulez voir à quoi ressemble une page HTML, c'est facile : vous allez sur n'importe quel site, disons au hasard python.org, et vous demandez à votre navigateur de vous afficher le code source. C'est ce que le navigateur reçoit, et qu'il sait comment afficher.

Ce qui m'intéresse avant tout ici ce sont les menus. Voilà le code HTML pour le menu principal, et si vous ne connaissez pas encore HTML vous pouvez remarquer qu'il y a partout des mots-clefs entre des signes inférieur et supérieur, que l'on appelle des balises. Le navigateur ne les affiche pas, ce sont des commandes pour lui. Elles vont d'habitude par paires, la balise de fin commençant par inférieur barre de division.

Le menu est compris entre des balises UL, qui signifie unordered list, liste non ordonnée. Chaque entrée dans le menu est entre des balises LI, pour list item, élément de liste.

Et à l'intérieur de chaque paire de balises LI, on trouve une balise A, pour anchor, ou ancre. Cette balise indique un hyperlien – c'est-à-dire en général ce sur quoi il faut cliquer pour aller sur une autre page. A l'intérieur de la balise, on trouve HREF qui indique soit sur quelle page, soit comme ici dans quel répertoire aller. Et entre les balises, eh bien on va avoir ce que le menu affichera et ce sur quoi on va cliquer.

Je récapitule. Dans une page HTML, un menu c'est souvent une paire de balises UL, entre lesquelles on a une paire de balise LI par choix possible, et entre lesquelles une paire de balise A dit où l'on va, et ce qu'il faut montrer à l'écran. L'apparence du menu est contrôlée par ce qu'on appelle des feuilles de style, avec souvent en prime un peu de programmation dans le langage qui s'appelle Javascript.

Mais revenons à Python. L'avantage de créer les pages HTML à la volée, de manière dynamique, c'est que l'on peut les adapter au visiteur. Un visiteur occasionnel verra un certain nombre de choses sur le menu. S'il rentre un nom et un mot de passe et se fait reconnaître comme membre, il verra d'autres options – par exemple il pourra poser des questions sur le forum, ou accéder à certaines sections. Certains utilisateurs, comme les modérateurs du forum, pourront faire d'autres choses, comme nettoyer le spam ou faire disparaître les messages des trolls.

Comment représenter un menu en Python ? Finalement un menu, c'est un ensemble d'intitulés, associés à des pages vers lesquels on est dirigé quand on clique sur l'intitulé. On pourrait utiliser des listes avec des éléments que l'on séparera comme nous l'avions fait dans le programme de questions-réponses. Mais comme j'ai une idée derrière la tête, je vais faire un tout petit peu différent. Je vous avais dit que dans une liste, les éléments pouvaient être n'importe quoi, y compris des listes : je vais représenter chaque option par une liste de deux éléments, l'intitulé et la page de destination.

Vous voyez bien comment je peux représenter mon menu, et bien entendu ajouter et enlever les options à volonté avec les opérations sur les listes que nous avons déjà vues.
Il est facile d'écrire une fonction qui produit le bout de code HTML qui irait bien pour afficher ce menu :

D'abord la balise ouvrante ul,

Puis je boucle sur les options, en récupérant les deux éléments d'un coup, c'est une facilité d'écriture que permet python.

Puis pour chaque option la balise ouvrante li, la balise a avec l'adresse, l'intitulé, la balise a fermante et la balise li fermante

Et en fin de boucle la balise ul fermante.

Je lance, impeccable, cela à l'allure de ce que nous avons vu sur python.org.

Mais maintenant, corsons un peu les choses : et si l'on avait des sous-menus ? C'est-à-dire que quand l'on clique sur une option, on ouvre un autre menu, au lieu d'aller sur une autre page. Le côté apparition/disparition est généralement géré par du Javascript. Au niveau du HTML, on a toute une arborescence, c'est-à-dire que l'on va retrouver, au lieu d'avoir directement des balises A entre les balises LI, l'intitulé, et un autre bloc menu entre des balises ul.

Comment représenter cela dans ma liste Python ? Très facile ! Là où j'avais l'emplacement d'une page, je vais substituer une autre liste représentant un menu, qui a la même allure générale que le menu. Il ne faut pas se tromper dans le nombre de crochets, mais sinon ce n'est pas plus compliqué que cela.

Sauf que maintenant, pour cracher du HTML, cela se complique.

Ce qui va nous sauver, c'est ce qu'on appelle la récursion. Le principe de la récursion, c'est de résoudre quelque chose de compliqué en fonction d'une version un peu moins compliquée de la même chose, jusqu'à arriver à quelque chose de vraiment très facile, puis revenir en arrière pour résoudre le problème initial.

Je vous montre, c'est plus facile à comprendre sur un exemple. Pour le menu, le cas facile c'est quand il n'y a pas de sous-menu – quand on est au dernier niveau. Afficher le sous-menu tout seul, on saurait bien faire ; c'est exactement ce que l'on vient de voir. Et quand il n'y a pas de sous-menu, on sait bien faire aussi.

Chaque fois que je récupère une option, je vais vérifier si cette option est ou non une liste, ce que je peux faire avec l'expression suivante :

if isinstance(option, list) :

Isinstance est une fonction qui retourne vrai ou faux selon que le premier argument est du type correspondant au deuxième ou pas.

Je vais commencer par le cas où ce n'est pas une liste, que nous savons traiter.

Et maintenant, que fait-on quand c'est une liste : eh bien c'est simple, on suppose que la fonction est déjà, si l'on peut dire, « fonctionnelle », et on l'appelle pour afficher le sous-menu.

On lance, et hop.

On peut faire plus joli, naturellement. Si vous vous rappelez, quand on applique la multiplication à du texte, on l'affiche autant de fois. Je vais ajouter un paramètre ‘décale’, de manière à indenter un menu en insérant ‘décale’ blancs en début de ligne. Cela ne changera rien pour le navigateur mais cela donnera une page HTML plus lisible.

Au début, mon décalage est de zéro. Quand je rappelle la fonction, je décale de 2 blancs de plus. Voilà ce que cela donne.

Et si je rajoute encore un niveau de menu, cela fonctionne toujours, magiquement !

La récursion, avec ses fonctions qui s'appellent elles-mêmes et qui ont l'air de se manger la queue, m'a toujours fait penser à quelque chose qui me fascinait quand j'étais petit garçon, l'illustration de la boite de vache-qui-rit avec la fameuse illustration de Benjamin Rabier sur laquelle les boucles d'oreilles de la vache sont faites avec la boîte sur laquelle on retrouve la même illustration, avec les boucles d'oreille.

La récursion est une méthode de programmation très puissante mais pas toujours très bien utilisée. Je l'ai vue expliquée beaucoup de fois, non pas par la vache-qui-rit, mais par les calculs de factorielles. Une factorielle, notée n point d'exclamation, c'est le produit d'un entier n par tous les entiers plus petits et c'est quelque chose que l'on emploie beaucoup en probabilité pour les calculs de nombres de combinaisons. On dit :

La factorielle de 1 est 1 (c'est le cas trivial) et pour n plus grand que 1 la factorielle de n vaut n que multiplie la factorielle de (n-1). Evidemment on peut traiter cela récursivement.
On peut dire que si n est supérieur à un, alors on retourne n que multiplie la factorielle de n – 1 – c'est l'appel récursif – sinon on retourne 1. Je laisse volontairement de côté la gestion de la validité des données pour ne pas alourdir.

Ce qui va se passer, c'est que quand je vais calculer la factorielle de 4, je vais dire c'est 4 que multiplie la factorielle de 3, que je ne connais pas, et ainsi de suite jusqu'à trouver 1 ; A ce point là, je reviens et peux calculer la factorielle de 2, puis celle de 3, puis enfin celle de 4.

Sauf que comme exemple de récursion c'est nul, pour ne pas employer un mot plus grossier. En effet, on connait parfaitement les points de départ et d'arrivée. Pour quoi remonter jusqu'à 1, puis redescendre, plutôt que de partir directement de 1 ? La bonne manière de calculer la factorielle de n, c'est une simple boucle - pas d'appel récursif, qui rajouterait du travail pour rien.

f = 1
for i in range(2,n):
	f = f * i

La factorielle, c'est quelque chose de relativement linéaire. Calculer en fonction d'une autre factorielle peut être intéressant si l'on mémorise, par exemple dans une liste, des résultats que l'on saura réutiliser. Sinon, cela n'a aucun intérêt. La récursion, c'est pour quand cela part dans tous les sens et que l'on ne peut pas traiter facilement : pour un menu, on ne sait pas à l'avance où seront les sous-menus, ni combien il y en aura, ni quelle profondeur de menus on doit affronter.

Impossible de traiter cela simplement avec une boucle.

Autre cas intéressant de récursion que nous verrons dans le prochain épisode, les tris.


Vidéo Récursion (2/2) : trier

Récursion (2/2) : trier

Transcript Vidéo

Continuons sur le thème de la récursion, avec des exemples pratiques et concrets pour bien visualiser le fonctionnement de ce principe.

Vidéo

Récursion (2/2) : trier Transcript

http://www.dailymotion.com/swf/video/xdu4zc_tech


Récursion (2/2) : trier Transcript

Transcript

Vidéo Objets et méthodes

Bonjour,

Après vous avoir présenté la récursion, je voudrais vous en montrer un exemple plus complexe, que j'espère que vous allez trouver intéressant, qui va, comme pour les menus, mettre en jeu une liste de listes – mais à un seul niveau cette fois ci.

Prenons l'exemple d'un carnet d'adresses – un carnet d'adresses, c'est plusieurs informations pour chaque entrée. Dans un programme de mail, ce pourrait être le prénom, le nom, l'adresse e-mail, et, pourquoi pas, un numéro de téléphone et d'autres informations. Les informations liées à une personne peuvent donc être décrites comme une liste. Sauf que bien sûr, dans un carnet d'adresses vous allez avoir plusieurs correspondants – donc une liste de listes. Au résultat, votre carnet dans un programme Python finira par ressembler à quelque chose comme cela :

carnet = [['Homer', 'Simpson', '[email protected]', '012345678'],
          ['Bart', 'Simpson', '[email protected]'],
          ['Lisa', 'Simpson', '[email protected]'],
          ['Milhouse', 'van Houten', '[email protected]'],
          ['Ralph', 'Wiggum', '[email protected]'],
          ['Rod', 'Flanders', '[email protected]'],
          ['Janie', 'Hibbert', '[email protected]'],
          ['Cletus', 'Spuckler', '[email protected]', '0987654321']]

Comme chacun sait, un carnet d'adresse se doit d'être trié quand on y recherche quelque chose. Et là, problème. Certes nous avons la méthode sort() pour trier une liste, mais si nous l'appliquons à notre carnet :

carnet.sort

Nous obtenons certes la liste triée, mais par prénom :

['Bart', 'Simpson', '[email protected]']
['Cletus', 'Spuckler', '[email protected]']
['Homer', 'Simpson', '[email protected]']
['Janie', 'Hibbert', '[email protected]']
['Lisa', 'Simpson', '[email protected]']
['Milhouse', 'van Houten', '[email protected]']
['Ralph', 'Wiggum', '[email protected]']
['Rod', 'Flanders', '[email protected]']

Évidemment, on peut se dire “eh bien, il n'y a qu'à mettre nom, puis prénom”. Sauf que parfois, quand on ne se rappelle pas très bien le nom de famille, ou que l'on a des incertitudes sur son orthographe, on peut aussi être intéressé par un tri par prénom. Je suis à peu près certain que comme moi, il y a des gens dont vous ne connaissez que le prénom. En fait, les deux tris peuvent être intéressants, et ce serait dommage d'avoir un ordinateur et ne pas être capable de trier les données comme on le veut ! Puisque nous savons comment trier la liste par prénom, écrivons une fonction qui nous permettra de trier par nom.

Tout ce qui est recherche et tri est ultra-important en informatique. Dès que vous recherchez la moindre chose sur le web (y compris Youtube), il y a d'abord identification de ce qui correspond à votre recherche, puis des tris pour essayer de vous montrer d'abord ce qui a le plus de chances de vous intéresser. Les méthodes de tri, il y en a plusieurs. Cela va peut-être vous surprendre, mais il y a des livres entièrement dédiés à la question. Celui-ci, par exemple, est un classique, et son auteur, Don Knuth, est une des rock-stars de l'informatique. Ce volume fait plus de 700 pages bien tassées et si vous entretenez avec les mathématiques des relations courtoises, mais distantes, ce n'est sans doute pas un livre pour vous.

Mais je vais quand même essayer de vous en distiller un petit quelque chose !

Le gros problème des tris c'est qu'ils tiennent mal la charge : cela demande plus de 100 fois plus de travail pour trier 100 fois plus de valeurs. Et si l'on utilise un mauvais algorithme, cela peut être 10,000 fois plus de travail.

Pour simplifier très grossièrement, le coût du tri va dépendre du nombre de comparaisons que l'on va faire ; plus on effectue de comparaisons, plus cela va prendre de temps. Il y a d'autres choses qui interviennent, comme le nombre de permutations que l'on devra effectuer, mais le nombre de comparaisons est un bon indicateur.

Je vais vous montrer un tri non récursif assez classique, le tri par minimums successifs. L'idée est de commencer par trouver la valeur la plus petite. On dit d'abord que la valeur la plus petite est la première. Puis on compare à toutes les valeurs qui suivent, et si l'on en rencontre une plus petite que la plus petite vue jusqu'à présent, et bien on mémorise que c'est celle là. Quand on est arrivé au bout de la liste, on est certain d'avoir la plus petite, et on la met au début en l'échangeant avec la première. Et après ? Eh bien on oublie la première et l'on recherche la deuxième plus petite en partant de la deuxième valeur, et ainsi de suite.

On voit que pour trouver la première valeur, s'il y en a n dans la liste on fait n – 1 comparaisons ; pour la deuxième, n – 2 comparaisons et ainsi de suite.

Au total on aura (n-1) + (n-2) + (n-3) + … + 1 comparaisons, ce qui se calcule comme valant n x (n – 1) /2 .

Il y a cinquante ans par un jeune mathématicien anglais de 26 ans, Tony Hoare, a inventé une méthode de tri qu'il a appelée quicksort, qui veut dire tri rapide, et qui est à ce jour restée la plus efficace des méthodes de tri générales. Tony Hoare est aujourd'hui Sir Antony Hoare, un vieux monsieur anobli par la Reine, ce qui était mérité, et une autre des légendes vivantes de l'informatique.

L'idée géniale de Hoare a été de se dire que puisque cela coûte plus de deux fois plus de temps pour trier deux fois plus de données, eh bien si au lieu de trier n valeurs on en trie 2 fois la moitié, c'est-à-dire n sur 2, on va être gagnant.
On va donc prendre la première valeur, comme précédemment la comparer à chacune des autres valeurs, mais pour faire deux paquets avec les autres valeurs :

Les valeurs plus petites, et les valeurs plus grandes

Ensuite, on va récursivement trier chacun des paquets (qui évidemment ne seront de taille égale que si par le plus grand des hasards la valeur choisie au début tombe au milieu de la liste).

Je vais vous comparer les coûts, encore une fois en me limitant au nombre de comparaisons, du tri par minimums successifs et du quick sort.

Pour le tri par minimums successifs, je vous l'ai dit, le coût est de n (n-1)/2.

Pour le quicksort, c'est plus complexe, et l'on va le calculer de manière récursive.

D'abord le cas trivial. Si l'on a 0 ou 1 valeur, il n'y a aucune comparaison.

S'il y en a deux, une seule comparaison.

S'il y en a davantage, je ne sais pas quelle sera la taille de mes paquets de valeurs plus petites et plus grandes. Une analyse mathématique stricte serait difficile, et je vais faire de nouveau appel au module random que nous avons vu pour les quizz et en utiliser la fonction randint qui retourne un entier aléatoire.

Si je dis que j'ai n1 valeurs plus petites que la première, n1 peut être n'importe quoi entre 0 et n – 1. Le nombre de valeurs plus grandes que la première sera n – 1, moins n1.

Mon nombre de comparaisons sera donc n – 1 comparaisons pour construire mes deux listes de valeurs plus petites et plus grandes, plus les comparaisons pour trier les n1 valeurs plus petites, plus les comparaisons pour traiter les n2 valeurs plus grandes.

Je vais afficher en essayant de faire joli. J'utilise ici la fonction format. Ce n'est pas très important, mais dans un texte j'indique avec des accolades là où je vais mettre les valeurs passées en arguments à format. 1 correspond au deuxième paramètre. Après deux points, un signe supérieur indique que je veux aligner à droite (inférieur alignerait à gauche, et accent circonflexe centrerait), 20 est la largeur de ma colonne, et s veut dire que c'est du texte. Pour un entier il faut mettre d.

Je boucle sur des puissances de 10, de 1 à 5, et voilà le résultat. Evidemment, la fonction randint fera que deux exécutions successives me donneront des résultats différents pour le quicksort, mais pas très différents. Vous voyez qu'il n'y a pas photo. Quand le tri par minimums successifs demande presque 50 millions de comparaisons pour trier dix mille valeurs, le quicksort est dans les 10 à 15000 comparaisons.

Pour coder le quicksort sur mon exemple, je vais commencer par coder une fonction de comparaison, vient_avant, qui retourne vrai ou faux suivant que le premier argument doit venir avant le second ou pas. Rappelez-vous que mes arguments sont des listes, prénom puis nom. Si les noms sont différents, je retourne True ou False (attention, majuscule obligatoire) suivant l'ordre des noms de famille. Si le nom de famille est identique, je trie suivant le prénom.

Maintenant le tri. Je commence par regarder le nombre d'éléments dans ma liste. 0 ou 1, je retourne ma liste telle quelle. Deux éléments, je les compare, et s'ils ne sont pas dans le bon ordre je les intervertis. En informatique, quand on intervertit, on a besoin d'une autre variable, c'est comme quand on transvase des liquides.

S'il y a plus de deux éléments, je prends le premier élément comme référence, je crée une liste pour les éléments plus petits, une pour les éléments plus grands, et je boucle pour mettre mes éléments dans l'une ou l'autre liste.

Au final, je retourne les éléments plus petits triés, je rajoute mon élément de référence, puis je rajoute, avec la méthode extend qui me permet de mettre bout à bout des listes, les éléments plus grands, eux aussi triés.

C'est fini, et j'espère que vous n'aurez pas eu besoin d'aspirine.

Je reprends mon exemple, je trie, je boucle pour afficher,

Et voilà le travail.


Vidéo Objets et méthodes

Objets et méthodes

Transcript Vidéo

Nous allons maintenant découvrir le principe de la programmation orientée objet, une technique de programmation célèbre qui nous permet de travailler de façon plus efficace. Nous allons manipuler des objets dans notre programme et effectuer un certain nombre d'opérations dessus à l'aide de fonctions particulières appelées méthodes.

Vidéo

Objets et méthodes Transcript

http://www.dailymotion.com/swf/video/xdu4x9_tech


Objets et méthodes Transcript

Transcript

Vidéo Objets encore...

Bonjour,

Nous avons vu comment créer des fonctions, nous n'avons pas vu comment créer des méthodes. Les méthodes sont attachées à ce qu'on appelle des objets, c'est-à-dire des types de variables plus complexes qu'entier, décimal ou caractère. Les listes sont des objets. Le texte aussi, puisqu'un texte n'est finalement rien d'autre qu'une liste de caractères. C'est ce qui explique que l'on retrouve un certain nombre d'opérations communes entre texte et liste, telles que la possibilité d'en calculer le nombre d'éléments avec la fonction len() , ou d'en extraire des parties, ou d'y rajouter quelque chose.

En informatique, ce qu'on appelle objet est l'association de une ou plusieurs variables, éventuellement elles-mêmes complexes, qui décrivent des propriétés, et de fonctions qui n'ont de sens qu'appliquées à cet objet. Les variables sont appelées des attributs, et les fonctions sont des méthodes.

Je vais tout de suite définir un objet, et commencer à l'utiliser.

Pour définir un type d'objet, on utilise le mot-clef class , suivi du nom que l'on donne à ce genre particulier d'objet. Disons que j'appelle mon objet lapin.

A l'intérieur de la définition de type, je vais définir, sous forme de fonctions, les méthodes qui sont valides pour l'objet. Je les définis comme toute bonne fonction, avec le mot-clef def et bien sûr en indentant parce que la définition de la méthode est à l'intérieur de la définition de l'objet.

La première fonction que je définis est très spéciale : elle s'appelle __init__ , avec deux caractères de soulignement devant et deux caractères de soulignement derrière.

Que se passe-t-il d'habitude quand je dis :

nom = 'Homer'

En fait, python reconnaît, grâce aux apostrophes, que je veux mettre dans la variable nom du texte, et que ce texte que j'affecte dans la variable vaut Homer. Il va réserver de la place en mémoire pour stocker le texte, mémoriser ailleurs le nom de la variable si elle n'existe pas déjà, et associer les deux. Quand je vais affecter un objet de type lapin à une variable, je vais dire quelque chose comme :

jeannot = Lapin(...)

avec éventuellement entre parenthèses un ou plusieurs attributs typiques de mon lapin, de la même manière que Homer était ce qui était particulier à mon texte. Pour python, il va falloir réserver de la place dans la mémoire pour garder trace des attributs. C'est exactement la fonction de __init__ , qui est la méthode qui crée l'objet.

Toutes les méthodes ont au moins un paramètre qui a un nom particulier : self. Self veut dire « soi-même » en anglais – c'est ce que l'on retrouve dans « self-service ». « self » est très, très spécial : il n'apparaît jamais quand on invoque la méthode. C'est un paramètre implicite dans l'appel d'une méthode (en fait, c'est ce qu'il y a avant le point qui sera passé) mais qui doit être explicite dans les définitions. Après ce paramètre viennent ceux qui caractérisent l'objet que l'on crée. Pour notre lapin, nous n'allons en considérer qu'un, le sexe – c'est vendeur. On appellera avec M pour créer un lapin mâle, et F pour créer une lapine. Et que fait-on dans __init__ ? Eh bien on va mémoriser le sexe du lapin en disant :

self.genre = sexe.upper()

genre est, dans ce cas particulier, l'unique attribut du lapin, et je le préfixe avec self point pour dire : « ce lapin précis que je suis en train de créer » - tout comme nous préfixions les méthodes de la liste ou du texte auquel nous l'appliquions. J'utilise .upper() pour être certain d'avoir une majuscule. Si je voulais bien faire les choses, je testerais que j'ai bien M ou F et je lèverais l'exception ValueError si j'avais autre chose.

Bien entendu je pourrais définir d'autres attributs pour mon lapin – couleur, date de naissance, poids … toutes choses que je traiterais de la même manière. Je pourrais aussi affecter une valeur à une variable de la classe à l'extérieur d'une fonction, par exemple

faim = True # attention à ne pas oublier la majuscule de True

pour dire que quand je crée un lapin il est toujours affamé. Mais contentons-nous pour l'instant d'un seul attribut.

Je n'ai rien d'autre à faire dans __init__ . Juste affecter les attributs de mon objet à la création. Le fait de commencer par un ou deux caractères « souligné » est une convention dans python, qui dit « ceci est juste pour la cuisine interne, ce n'est pas une méthode que l'on est sensé utiliser explicitement ». __init__ n'est qu'une parmi toute une série de fonctions un peu magiques aux noms prédéfinis.

Par exemple, quand vous créez un nouveau lapin, il y a une fonction __new__ qui est appelée pour réserver de la place (si je vous en parle, ce n'est pas innocent, nous la reverrons), puis __init__ pour affecter des valeurs aux attributs. Si l'on ne les redéfinit pas, __new__ se contente de réserver de la place en fonction de la taille des attributs définis, et __init__ par défaut ne fait rien. Il y a aussi une méthode __del__ qui peut être appelée quand python décide que l'objet n'est plus utilisé et que la mémoire peut être libérée. Cela peut être utile quand certains attributs de l'objet sont eux-mêmes des objets complexes. Il y a encore d'autres fonctions similaires.

Nous avons un embryon de classe, testons-la.

Créons un lapin que l'on appelle jeannot. Jusque là, tout va bien. Affichons-le. Pas d'erreur, mais un message bizarre. Lapin, on sait ce que sait, la classe, autrement dit le type de donnée, que l'on vient de créer. Instance, c'est une variable particulière de ce type. Et ce qui suit « at » est une adresse mémoire, qui indique où Python a été mettre les valeurs attachées à cette variable. Pour Python, un lapin est quelque chose de valide – mais qu'il ne sait pas afficher proprement.

Vous verrez parfois dans des définitions de classe une explication comme

Ceci n'est pas vraiment un lapin

Coincé entre 6 guillemets, 3 d'un côté et 3 de l'autre. C'est un genre de commentaire. Pas tout à fait un commentaire, parce que si j'ai défini ma classe avec ce commentaire un peu spécial, je peux écrire

print jeannot.__doc__

Et l'afficher. Au passage, vous pouvez essayer ce double souligné doc double souligné avec n'importe quel type de variable.

Mais print jeannot m'affichera toujours la même chose.

Si je veux contrôler l'affichage, il me faut définir une autre fonction magique, qui a pour nom double souligné repr double souligné, qui est l'abréviation de « représentation ». Cette fonction va simplement contrôler le genre de mon lapin, et retourner « lapin mâle » - comme j'ai un accent circonflexe il me faut l'incantation qui permet d'avoir des accents - ou « lapin femelle » suivant le cas.

Pour tester je crée flopsaut, une lapine, je l'affiche, et j'ai ce que je voulais.

Je peux définir ma représentation exactement comme je le veux. Par exemple, j'aurais pu la définir comme ça. Vous devez vous demander ce que c'est que ce truc.

Je vous montre tout de suite

J'espère que vous voyez mieux…

Bon, ce n'est pas tout, vous allez me dire « on a écrit plein de fonctions magiques, mais où sont les méthodes ? ». J'y viens.

Je vais écrire une méthode partage_clapier . Si j'ai un lapin mâle, et que je mets dans son clapier un autre lapin mâle, ce sera sans doute excellent s'il est d'ordinaire dépressif mais il ne va pas se passer grand-chose. Si en revanche j'ôte le lapin mâle pour mettre un lapin femelle, là, ...

Tirons un voile pudique sur la vie privée de nos lapins, si je le rouvre un mois plus tard j'aurai sans doute d'autres lapins.

Écrivons la méthode. Comme les fonctions magiques, elle prend comme premier paramètre self , qui sera implicite quand on utilisera la méthode, et un deuxième paramètre qui est un second lapin. Il ne se passera quelque chose que si les lapins sont de genres différents. On aura une portée pour laquelle je crée une liste « petits », et je vais avoir un nombre au hasard de petits entre 3 et 12. Évidemment la fonction randint que nous avons déjà vu dans la programmation du quizz demande à être importée du module random .

Je boucle sur les petits, et je vais tirer au hasard 0 ou 1 pour déterminer le sexe du lapereau.

Si c'est 0, je vais dire que c'est un mâle, sinon une femelle.

En bout de course, je retourne la liste des petits – là encore, uniquement si les lapins sont de genres différents.

Vous remarquerez que l'on retrouve un peu de récursivité dans notre définition d'objet, puisqu'elle contient une méthode qui lui fait référence.

Testons. Je crée jeannot, un lapin mâle. Je crée pantoufle, un autre lapin mâle. Et je dis que jeannot et pantoufle partagent le même clapier – évidemment il ne se passe rien (dans l'histoire, je peux parfaitement intervertir jeannot et pantoufle dans l'invocation de la méthode).

Je crée gflopsaut, une lapine, je la mets dans le clapier de jeannot, et là boum, toute une portée qui au passage utilise automatiquement dans la représentation de la liste la fonction magique double souligné repr double souligné.

Nous continuerons sur les objets dans le prochain épisode, avec un exemple peut-être un peu plus sérieux…


Vidéo Objets encore...

Objets encore...

Transcript Vidéo

Continuons notre découverte du fabuleux monde des objets. Dans ce chapitre, vous allez découvrir une première approche de ce qu'on appelle l'héritage, mais ce sera aussi l'occasion de vous parler des conventions que les développeurs créent entre eux. C'est une façon de développer de façon plus homogène et donc plus lisible.

Nous allons pour nos exemples créer une classe qui gère des fractions. Comment ça fonctionne et à quoi ça peut servir ? Vous allez voir !

Vidéo

Objets encore... Transcript

http://www.dailymotion.com/swf/video/xezj7v_tech


Objets encore... Transcript

Transcript

Vidéo Objets toujours...

Bonjour,

Je voudrais poursuivre avec les classes et les objets pour, après le B-A BA, tout d'abord vous montrer une chose importante que nous n'utiliserons pas tout de suite, puis essayer de vous donner une petite idée de jusqu'où l'on peut aller en programmation objet – assez loin comme vous le verrez.

Première chose, il est commun, dans la vie courante, de définir un type d'objet à partir d'un autre type d'objet. Par exemple, on peut dire qu'un fauteuil est comme une chaise, mais plus large, et avec des bras. Cela m'évite de vous dire que ça sert à s'asseoir, que ça a un dossier et ainsi de suite.

De la même manière, à partir du moment où je vous dis qu'un pingouin est un oiseau, je ne vous ai pas tout dit mais je n’ai pas besoin de vous expliquer que c'est un animal bipède avec des ailes, qui pond des œufs, qui a un bec etc. Tout ce dont j’ai besoin de parler c'est des particularités du pingouin par rapport aux autres oiseaux. Définir une classe par dérivation d'une autre classe est une pratique très commune en programmation objet, c'est ce qu’on appelle le mécanisme d'héritage. On dit qu'une classe est un cas particulier d'une autre classe, et l'on définit des méthodes ou des attributs spécifiques ; on peut redéfinir des méthodes existantes, et l'on peut bien entendu dériver encore d’autres classes de celle là. L'avantage, c'est qu'il y a tout un tas de comportements classiques que l'on n'a pas besoin de reprogrammer. Il y a un domaine où cela saute aux yeux, et c'est quelque chose que j’aborderai prochainement, le domaine des interfaces graphiques. Quand vous avez une fenêtre, quelle qu'elle soit, vous la déplacez ou la redimensionnez de la même manière que toutes les autres fenêtres, vous savez comment l'iconifier, la maximiser ou la fermer. Bien entendu vous pouvez bloquer des opérations comme le redimensionnement mais, à la base, toutes les fenêtres héritent du comportement de LA fenêtre type.
Mais voyons ce que cela donne en pratique. Je vais créer une classe que j’appelle adresse_mail , et j’indique entre parenthèses le nom de la classe dont elle hérite, str .

Là, il faut que je m'arrête et que je glisse un mot à propos de ce que les Anglo-Saxons appellent CamelCaps, la « capitalisation chameau », pour des raisons visuellement assez évidentes. Il est d’usage, quand on nomme une classe, de ne pas utiliser de caractère souligné mais en fait de coller les mots en mettant en majuscule leur première lettre et en minuscules les autres. La majuscule n'est pas systématique, vous en avez un exemple sous les yeux avec str qui est tout en minuscules, mais c'est l'habitude. Bien entendu, pour python, cela ne change rien, c'est pour aider à comprendre à quoi on fait référence quand on lit le programme. Il est probable, si vous êtes amenés à coder de manière professionnelle, que vous rencontrerez des conventions d'écritures assez variables dans vos pérégrinations. Il y a un vieil adage, davantage utilisé en anglais qu'en français, qui dit « à Rome, fais comme les Romains ». Quand vous travaillez sur un programme existant qui respecte certaines conventions, respectez-les, même si elles ne vous emballent pas. En informatique, c’est important de rester cohérent.

Reprenons donc notre classe, ajustons le tir, et écrivons la méthode __init__ . On créera l'objet en invoquant le nom de la classe et en lui passant une adresse. Quand on hérite d'une classe complexe, il y a assez souvent un appel explicite à la méthode __init__ de la classe parente ; ici, ce n’est pas nécessaire. Je vais commencer par créer une liste que j’appelle composants, en coupant l’adresse au caractère @. Je préfixe composants par self. , parce que je veux que cela devienne un attribut de l’objet : c’est une caractéristique permanente d’un objet, à laquelle je pourrai accéder depuis une autre méthode. Quand dans une méthode je ne préfixe pas par self un nom de variable, celle-ci n'a d'existence que dans la méthode et disparaît quand la méthode a été exécutée.

Si ce que j'ai passé comme adresse n'est pas de la forme quelque chose @quelque chose, je lance une exception. Ensuite je sauve le deuxième élément de la liste dans un autre attribut, domaine, et je contrôle que dans le domaine j'ai au moins deux parties (il peut y en avoir trois, c'est courant en Angleterre ou au Canada). Enfin, je contrôle que la dernière partie du domaine, c'est-à-dire ce qui correspond au .com ou . code pays, ne contient pas plus de 3 caractères.
Ce n'est pas parfait comme contrôles, mais c'est juste à titre d’illustration. Je crée une deuxième méthode nom, qui peut accéder à la liste "composants" puisque je l'avais préfixée par self pour dire que c’est un attribut permanent de l'objet, et qui en retourne le premier élément. Maintenant voyons ce que cela donne. Je crée un objet de type AdresseMail en invoquant le nom de la classe et en lui passant l'adresse [email protected] C’est une adresse qui satisfait mes contrôles, donc la création de l'objet se passe bien. J'ai accès à l’attribut domaine, défini comme attribut permanent. Beaucoup de gens préfèrent n'accéder aux attributs que par des méthodes, mais il est accessible quand même. La méthode nom() marche bien, mais surtout, et c'est là que je voulais en venir, je peux appliquer à mon objet la méthode upper() pour tout passer en majuscules parce que j'ai dit qu'une AdresseMail c'est un cas particulier de str . J’ai hérité de la méthode. Si je n’avais pas dit que AdresseMail hérite de str , j’aurais eu une exception disant que l'instance (c'est-à-dire l’objet créé par invocation de la classe) n'a pas d'attribut 'upper' – vous pouvez essayer !
Ceci étant dit, je voudrais maintenant vous donner une idée de jusqu'où l'on peut aller avec l'exemple d'une classe de fractions. Disons le tout de suite, quand on a besoin d’objets particuliers, la première chose à faire c'est aller voir dans la documentation de référence de la bibliothèque Python. Il y a déjà une classe pour manipuler les fractions, et si dans le monde réel je faisais ce que je vais faire là, j'aurais toutes les raisons d'être terriblement confus. La manière normale de procéder, c'est trouver une classe standard proche de ce que l'on veut faire, et d'en faire hériter votre classe, qui adaptera le standard à votre cas particulier. Il ne faut pas réinventer la roue.

Mais je suis ici dans un exercice pédagogique, et à ce titre c’est intéressant de voir comment la programmation objet permet de traiter les fractions.

Commençons par voir comment se comporte ce qui existe en standard, pour nous en inspirer.
Le module, en pratique un fichier .py, s’appelle "fractions" au pluriel et en minuscules. La classe, elle, s’appelle, conformément aux conventions habituelles, Fraction au singulier et avec une majuscule. Je peux créer un objet fraction en invoquant le nom de la classe avec un numérateur et un dénominateur. L’afficher devrait vous donner quelques idées sur la tête de la méthode __repr__ . Autre essai, quand on crée un objet fraction, la fraction est automatiquement réduite. Je vais m'écarter de ce comportement, parce que supposons que mon but soit de faire un programme qui génère des exercices pour des collégiens. Si je leur demande combien font 2/3 plus 4/3, il faut que je fasse la différence entre ceux qui n'ont rien compris et répondent 6/6, ceux qui n'ont pas tout compris et répondent 6/3, et ceux qui répondent 2. En revanche, je vais ajouter une méthode pour réduire, qui n'existe pas dans la classe standard.
Revenons à la classe existante. Elle accepte aussi à la création que l'on passe du texte représentant une fraction – ou simplement une seule valeur, et dans ce cas le dénominateur vaut évidemment un. Pour avoir un point de bonus, essayez de trouver ce que j'aurais obtenu si j'avais oublié les apostrophes autour de 4/5 – j’en ai parlé dans une des toutes premières pythonneries.
Je vais créer ma classe que je vais appeler Frac, pour la distinguer de « la vraie » et faire court.

Je peux définir numérateur et dénominateur à ce niveau, c’est la même chose que de les référencer en les préfixant avec self dans __init__ , et préciser que quand je créerai un objet de type Frac, je mettrai d’office 0 dans l'un et 1 dans l'autre. Je trouve plus clair de préciser les attributs à ce niveau.

Passons à __init__ maintenant. Là, j'ai un problème. On l'a vu, la classe Fraction permet de passer soit séparément un numérateur et un dénominateur, soit seulement un entier, soit du texte qui représente la fraction. Une chose à laquelle on peut penser, c'est de fournir des valeurs par défaut – c'est-à-dire des valeurs à prendre si rien n'est précisé. Cela se fait, quand on liste les paramètres de la fonction, en mettant « égal une valeur » après le paramètre. Si je ne passe pas de dénominateur, eh bien il vaudra « un ».. Cela résout un problème, mais pas celui de distinguer entre l’entier qui est passé, par exemple 2, et la fraction sous forme de texte, par exemple '4/5'. Une solution possible est d’utiliser la fonction appelée isinstance() qui permet de tester si un objet (le premier paramètre) est de la classe ou type passé en deuxième paramètre et retournant True ou False suivant le cas. Au passage, si la classe que vous appelez pour créer l'objet (on dit aussi dans le jargon instancier, d'où le nom de la fonction) est dérivée d’une autre classe, le fait que l'objet est une instance de l’autre classe sera vrai aussi. Par exemple, si l'on teste si un objet créé par invocation de la classe AdresseMail est une instance de str, on obtiendra True , ce qui est logique quand on y réfléchit.

Je pourrais donc écrire quelque chose comme :
Si mon premier paramètre est une instance de str , alors je sépare les éléments sur la barre de fraction, je mets le premier élément converti en entier dans le numérateur, et le second dans le dénominateur.

Sinon, le premier paramètre va dans le numérateur, et le deuxième (qui vaudra un si rien n'a été passé explicitement) dans le dénominateur. J'ai fait l'économie de pas mal de tests pour abréger ; tester que le dénominateur n'est pas zéro ne ferait pas de mal.

Vous pouvez écrire une fonction __repr__ , cela devrait vous donner des résultats assez comparables à nos tests précédents avec la classe Fraction. Sauf que...
En fait, si vous codez avec isinstance() , pas mal de gens vont vous regarder d'un drôle d'air, parce que ce que vous aurez fait n'est pas souple du tout. Comment s'assurer que quelqu'un ne va pas passer comme premier paramètre du texte, et un deuxième paramètre quelconque ? Comment traiter le cas où la classe serait invoquée avec des nombres décimaux – invalides – comme paramètres ? Comment faire pour créer facilement un objet Frac() distinct à partir d'un objet Frac() existant (c'est important, je reviendrai là-dessus) ? On a quelque chose qui marche à peu près, mais l'à peu près, en informatique, ce n'est pas bon.

Nous verrons comment mieux faire dans le prochain épisode.

Nous n'en avons pas fini avec cette classe Frac ! Dans le prochain chapitre, nous allons continuer à l'améliorer et découvrir de nouvelles choses sur la programmation objet.


Vidéo Objets toujours...

Objets toujours...

Transcript Vidéo

Continuons avec notre classe Frac qui gère les fractions. Nous allons grandement l'améliorer dans ce chapitre et découvrir de nouveaux concepts de la programmation objet : la redéfinition des méthodes et opérateurs. Un concept vraiment puissant comme vous allez le voir !

Vidéo

Objets toujours... Transcript

http://www.dailymotion.com/swf/video/xezja8_tech


Objets toujours... Transcript

Transcript

Vidéo Les éléments de l'interface graphique

Bonjour,

Rappel de l’épisode précédent : nous sommes en train de créer, dans un but purement pédagogique, une classe Frac assez semblable, malgré quelques variations, à la classe Fraction qui existe dans la bibliothèque standard Python.

Ce que nous avons vu avec la classe Fraction, c'est qu'elle accepte indifféremment pour créer une fraction un couple (numérateur, dénominateur), du texte numérateur barre de division dénominateur, ou simplement un entier, auquel cas on suppose implicitement que le dénominateur vaut un. Accessoirement, elle accepte aussi les nombres décimaux, mais nous, nous ne les accepterons pas.
Mais ce n'est pas tout. Je peux aussi créer une fraction à partir d'une autre fraction, en faire un clone si vous voulez. Pourquoi est-ce important ? Un petit retour en arrière s'impose. On a vu il y a longtemps que si je créais une liste, je pouvais ensuite soit affecter 'liste'’ à une liste liste2, soit affecter à liste3 liste crochet deux points crochet. A ce stade, si j’affiche liste, liste2 ou liste3 je verrai la même chose. Mais si je change le deuxième élément de ma liste initiale, en le remplaçant par exemple par zéro, je vois que liste2 me montre le nouvel état alors que liste3 est restée dans l'état initial. Dans le premier cas, j'ai créé en quelque sorte un synonyme, alors que dans le deuxième j’ai créé un véritable clone.

Avec les objets, les méthodes peuvent modifier les attributs, et donc l'état de l'objet. Dans le cas des fractions je vous ai dit que je préférais ne pas réduire les fractions à la création. Il me faudra quand même une méthode pour réduire, qui modifiera mon numérateur et mon dénominateur initiaux. Pouvoir créer facilement des clones est important pour comparer un état « avant » et un état « après » par exemple.

Je vous ai dit que pour créer une méthode __init__ qui ait toute la souplesse voulue, on pouvait penser à utiliser la fonction isinstance() mais que ce n'était pas une très bonne idée. On peut faire nettement mieux. Ce que l'on peut faire, c'est dire que la méthode accepte un nombre variable d'arguments, ce que l'on indique par une étoile suivie d'un nom de variable.

Techniquement parlant, cela veut dire que l'on va récupérer ce qu’on appelle un tuple. Disons pour l'instant, pour ne pas compliquer, qu'un tuple est un objet qui a beaucoup de points communs avec une liste. Parmi ces points communs, une fonction et une méthode que je vais vous montrer appliquées à une liste.

Créons une liste qui contient à la fois des nombres et du texte. La fonction que je veux vous montrer s'appelle map() et s'emploie de la manière suivante : si j'affiche map() appliquée à la fonction str et à la liste, je convertis chacun des éléments en texte. Attention, ici str correspond bien à la fonction qui convertit en texte, et non la classe homonyme. En gros, c'est un peu comme si je bouclais pour appliquer la fonction à chacun des éléments, sauf que c'est une écriture beaucoup plus compacte.
La méthode, maintenant, est une méthode de texte que je ne vous ai pas montrée jusqu'à présent et qui s'appelle join() . Si j'affiche un caractère auquel j’applique la méthode join() à laquelle je passe le résultat de la fonction précédente, je récupère du texte, chacun des éléments de la liste séparés par le caractère auquel j'ai appliqué la méthode.

Cela fait certainement un peu recette de cuisine de vous présenter les choses comme cela, mais si à la place d’avoir utilisé un tiret j'avais utilisé une barre de division, vous devez commencer à voir où je veux en venir et ce que je pourrais obtenir avec une liste contenant soit deux nombres, soit un seul élément texte représentant une fraction.
Revenons donc à __init__ . J'applique map de str à args, sans étoile ce coup ci, puis j'applique la méthode join à une barre de division et je mets le résultat dans une variable truc. Je reconnais que ce n'est pas terrible comme nom de variable mais je ne me sens pas très inspiré. De toute façon, je vais tout de suite l'exploser dans une liste, en séparant mes éléments sur, justement, la barre de division. Et là, je contrôle tout de suite combien j'ai d'éléments. En fait, tout ce que j'accepte c'est un ou deux – numérateur et dénominateur ou numérateur tout seul. Si je n'ai pas ça, je lance une exception. Sinon, le premier élément est forcément mon numérateur, que je mets dans num précédé de self. puisque c'est un attribut de l'objet. Si j'ai une seule valeur, j'ai fini puisqu'à la création de l'objet Frac je mets automatiquement 1 dans le dénominateur. Si j'en ai deux, la moindre des choses c’est de contrôler que le dénominateur n'est pas zéro, et sinon j'ai fini. Vous voyez que j’ai évité isinstance() et cela, grâce à la transformation systématique en texte et les différentes opérations de re-découpage, fonctionne avec des entiers, du texte, ou un autre objet de la classe Frac() (au passage, je pourrais aussi créer un objet de classe Frac() à partir d’un objet de classe Fraction()).

Question bonus : je ne veux pas de nombres décimaux comme paramètres, et je n'en aurai pas (la classe Fraction, elle, essaie de les transformer en fractions). Si vous pouvez trouver où les nombres décimaux vont provoquer des exceptions, c’est que vous avez une bonne mémoire...

Je vais vous montrer en action le clonage d'un objet de la classe Frac. Je crée un premier objet 4/5, et je l'affiche ainsi que le résultat de la fonction id() appliquée à cet objet. Id() , l'identité de l'objet, est un nombre (en pratique, une adresse mémoire) qui permet d'identifier de manière unique un objet ou une variable. Si j'affecte f1 dans f2, en affichant f2 je vois que j'ai la même chose que pour f1. En revanche, si je dis que f3 est égal à Frac(f1), je crée un nouvel objet et implicitement je rappelle la méthode __init__ . On voit que si la valeur de la fraction est la même (c'est heureux), l'identifiant a changé et l’on a bien une copie distincte de l’objet initial.

Je pense qu'au stade où nous sommes arrivés, je peux vous confier une partie du développement sans vous tenir la main. Si ce n'est déjà fait, je vous laisse écrire __repr__ , en n’oubliant pas de considérer le cas où le numérateur vaut 0 et celui où le dénominateur vaut 1.

Je vous laisse aussi écrire une méthode « privée » qui retourne le PGCD de deux entiers. Dans la classe Fraction c’est, sous son acronyme anglais GCD, une méthode « officielle » mais j'aime autant la cacher. Cherchez sur Internet « Algorithme d’Euclide », vous allez tout de suite tomber dessus. L’algorithme peut s'écrire avec une boucle ou de manière récursive ; dans ce cas précis on peut considérer que l’écriture récursive est un chouïa plus simple, je vous conseille de l’écrire des deux manières pour vous faire votre propre opinion.

Et bien entendu, je vous laisse écrire la méthode reduit() , qui est facile à écrire quand on dispose du PGCD...

En ce qui me concerne je voudrais vous montrer quelque chose qui va, j'espère, vous faire comprendre toute la puissance de la programmation objet. On peut redéfinir les opérations dans le langage, en fonction des objets auxquels on les applique (si vous entendez parler de « surcharge des opérateurs », cela veut dire la même chose).

Si je définis dans ma classe une méthode __add__ qui prend comme paramètres, outre l'inévitable self, un autre paramètre, chaque fois que Python rencontrera f, un objet correspondant à cette classe plus autre chose, il le calculera par appel de la méthode __add__ appliquée à l'autre chose.

Écrivons donc cette méthode pour la classe Frac . Première chose, si par définition self est un objet de classe Frac, on ne sait pas du tout ce qu'est 'autre' – un autre objet de classe Frac, ou pas. Je vais donc en faire une fraction. Si c'est quelque chose que je ne sais pas ou ne veux pas transformer en fraction, j'aurai une exception à ce niveau. Nous avons donc maintenant deux fractions, que je sais additionner. Si la première correspond à 'self' et la deuxième à f, je sais calculer sans problème le numérateur et le dénominateur. Avec cela, je peux créer la fraction résultat. Comme ceci n'est pas directement entré par l’utilisateur, je réduis ce résultat à l’aide de la méthode que je vous ai laissé écrire, et je le retourne. Fini !

Voyons ce que cela donne. Je crée une première fraction f1 qui vaut 2/3, une deuxième f2 qui vaut 3/5, j’affiche la somme, et j'obtiens le résultat 19/15. Je calcule f1 plus 1, 5/3 c’est parfait, 2 plus f2 et là, zut, ça ne marche plus. Que dis le message ? « unsupported operand types for + : 'int' and 'instance' » 'instance' est un terme générique pour un objet d'une classe que Python ne connaît pas. Où est le problème ? C’est que 2 n'est pas un objet de classe Frac. Frac(2), oui, mais pas 2 tout court. 2, comme le dit le message, c’est un 'int', un entier, qui n’a pas de méthode __add__ qui lui dise comment lui ajouter une fraction.

Que fait Python lorsqu'on lui demande d'additionner un objet de type A et un objet de type B ? Il va regarder si l'objet de type A possède une méthode __add__ qui lui permet d'ajouter un objet de type B. Quand mon objet de type A est une fraction et l'objet de type B une autre fraction, un entier ou du texte qui représente une fraction - c'est le cas, et je n’ai pas de problème. En revanche, si mon objet de type A est un entier et l'objet de type B une fraction, problème. Mais avant de lancer une exception, Python va effectuer un second contrôle : il va vérifier si l’objet de type B n'a pas une méthode __radd__ (où r signifie « right » et s’applique quand l’objet B est à droite), une méthode qui sait ajouter à un objet de type B un objet de type A.

Si je veux retrouver la commutativité de l’addition qui m'est chère, il me suffit de définir une méthode __radd__ dans la classe Frac. Ce que j'écris là n'est pas optimal, mais je fais simple : je convertis « autre », qui est ce que j'ai à gauche du signe plus, en une fraction f. A partir de là, f possède une méthode __add__ qui sait, lui, ajouter une autre fraction à sa droite, à savoir « self » en l'occurrence (j’espère que vous me suivez toujours). Il me suffit donc de retourner ce résultat. Je peux, avec mes deux méthodes __add__ et __radd__ lancer un autre test, où je crée une fraction qui vaut 3/4. F + 1 fonctionne bien, pas de surprise, et 1 + F tout aussi bien. J’ai donc maintenant non seulement des fractions, mais j'ai complètement redéfini l'addition pour opérer sur elles.

Ce que je viens de faire pour l'addition, je peux le faire pour toutes les opérations de base, sub pour la soustraction, mul pour la multiplication, div pour la division. Toutes ont une version « r quelque chose » associée. Il n'y a pas que les opérations de base. Je peux également redéfinir élévation à une puissance, division entière, modulo – je vous renvoie à la documentation de référence pour voir tout ce qui est possible.

On peut redéfinir non seulement les opérations de base, mais les opérateurs de comparaison (qui retourneront non pas un résultat mais True ou False ). Lt, lesser than, pour strictement inférieur, le, lesser or equal, pour inférieur ou égal, eq, equal, pour l’égalité, ne, not equal, pour l’inégalité, gt, greater than, pour strictement supérieur, ge, greater or equal, pour supérieur ou égal. Encore une fois, pour chaque méthode vous pouvez avoir la méthode « r quelque chose » associée. Définir ces opérateurs vous permettrait par exemple d’appliquer la méthode de tri sort() à une liste de fractions.

Comme vous le voyez, il y a plein de choses à faire pour obtenir une classe de qualité industrielle, et je vous laisse étoffer la classe Frac(), ou toute autre classe que vous pouvez imaginer, avant de commencer à nous frotter aux interfaces graphiques.


Vidéo Les éléments de l'interface graphique

Les éléments de l'interface graphique

Transcript Vidéo

Les interfaces graphiques, vous connaissez ? Toutes les fenêtres que vous avez l'habitude de manipuler, avec leurs boutons, leurs images, leurs menus, sont ce qu'on appelle des interfaces graphiques.

Jusqu'ici, nous avons seulement manipulé la console Python, ce qui n'était pas très excitant il faut bien l'avouer. Heureusement, nous sommes maintenant en mesure de créer nos propres interfaces graphiques (on parle aussi d'IHM : Interface Homme-Machine). On commence dès ce chapitre, suivez-moi ! :)

Vidéo

Les éléments de l'interface graphique Transcript

http://www.dailymotion.com/swf/video/xfd40s_tech


Les éléments de l'interface graphique Transcript

Transcript

Vidéo Réagir aux événements

Bonjour,

Je voudrais maintenant vous montrer comment la programmation objet permet de réaliser une interface graphique. Je m'empresse de dire que le sujet est vaste, et que je ne ferai que l'effleurer. Je vais m'attacher essentiellement à expliquer les grands principes de la programmation des interfaces graphiques. Et au-delà de Python, si vous êtes un jour amenés à programmer en Java avec Swing, en C avec le Gimp Tool Kit, GTK+, ou en C++ avec – tiens, peut-être des choses que nous allons voir là – vous retrouverez toujours les mêmes idées. Il y a dans les bibliothèques de fonctions graphiques des tonnes et des tonnes de trucs, la doc est un peu dure à aborder parfois, et j'essaierai de faire ici de même que ce que j'ai fait jusqu'à présent, à savoir vous mettre le pied à l'étrier.

J'aurai une approche minimaliste, c'est-à-dire que je m'appuierai le plus possible sur les valeurs par défaut. Beaucoup de fonctions prennent plein de paramètres, j'utiliserai et ne parlerai que du minimum nécessaire.

Autre chose, la vidéo, c'est sympa mais, personnellement, quand j'ai à montrer un morceau de code conséquent je me trouve un peu à l'étroit. Comme je vais vous montrer du code que j'améliore en plusieurs passes et sur lequel je reviens parfois, je ne montrerai que des morceaux avec un peu de contexte pour vous aider à vous repérer. Cela vous demandera une certaine attention, pour laquelle je vous remercie par avance.

Un programme python graphique a en général un nom terminé en .pyw au lieu du .py habituel.

Je vais m'attaquer à écrire une version graphique du programme de quizz de la Pythonnerie n°8, version améliorée dans la Pythonnerie n°9. Dans cette dernière, on lisait un fichier texte dont la première ligne contenait une consigne et les lignes suivantes des couples de questions et de réponses séparées par deux points. Après avoir lu le fichier dans un tableau on bouclait en posant des questions piochées au hasard en attendant que l’utilisateur entre comme réponse un mot qui signifiait qu'il voulait arrêter. La chose la plus évidente dans une application graphique, c'est que cette boucle disparaît. Quand vous voulez sortir d'une application graphique, vous fermez la fenêtre et c'est tout. En fait, votre programme va passer son temps à attendre que l'utilisateur tape quelque chose au clavier, fasse quelque chose avec sa souris ou même, dans le cas d’un ordinateur de type tablette, utilise son doigt.

Il faut bien comprendre que votre application va s'exécuter dans un environnement, celui d'un programme système qui est le gestionnaire de fenêtres, et qu'en fait c'est ce dernier qui va jouer les intermédiaires entre l'utilisateur et votre programme. Le gestionnaire de fenêtres mémorise la position de toutes les fenêtres et des éléments sur lesquels vous pouvez cliquer. Il traque aussi en permanence la position de votre curseur, puisqu'il faut bien le dessiner, et le redessiner ailleurs si vous bougez votre souris. Quand vous cliquez à un endroit, il va calculer si c'est sur une fenêtre. Si c’est le cas et que ce n'est pas la fenêtre active, il va changer la couleur du bandeau de cette dernière, faire la même chose, mais en sens inverse, avec la fenêtre qui devient active, et puisque souvent elle était « cachée » par la fenêtre active il va lui dire de se redessiner pour remettre sur l’écran ce qu’elle contenait. Le résultat vous semble peut-être naturel mais n'est pas si évident que ça. On parle de programmation événementielle, parce qu'en fait, on passe son temps à attendre un événement, en général une interaction de l'utilisateur, parfois un événement produit par un programme, et réagir par rapport à cet événement. Nous verrons cela dans les vidéos suivantes, pour l'instant attachons nous à l'apparence de notre application. Partons avec un crayon, et disons pour l'instant que l'on veut une fenêtre, mettre un score en haut, afficher la consigne en dessous, puis la question, une zone où les gens peuvent taper leur réponse, et un bouton pour qu'ils puissent dire « voilà ma réponse ». Ce sera notre première version, et dans cette vidéo j'oublierai temporairement le score. Le problème avec Python c'est qu'il y a trois environnements graphiques principaux. Un qui s’appelle Tkinter, hérité d’un autre langage de script, Tcl/Tk (les Anglo-Saxons disent Tickle – Teekay). Un qui s’appelle WxPython, dérivé d’une bibliothèque C++, et un troisième qui s’appelle PyQt qui dérive d’une autre bibliothèque graphique en C++, Qt, développée par une société norvégienne (TrollTech) qui a depuis été rachetée par Nokia. Si vous connaissez un peu Linux, l’environnement KDE est à base de Qt. Tkinter, le plus ancien, est un peu plus rustique, mais tous trois sont des choix parfaitement respectables.

Les principes sont en gros les mêmes, mais il y a plein de différences de détail. Un des points communs aux trois environnements est une doc un peu ardue. Je vais en choisir un, ce qui m'assure de faire râler pleins de gens qui auraient préféré que j’en utilise un autre. Bon allons y, j’importe ma fonction choice , je crée une liste avec les noms des environnements graphiques, j’en tire un, et hop ce sera wxPython. On le trouve sur wxpython.org, et vous devez télécharger la version qui correspond à la fois à votre système et à votre version de Python.

Dans la prochaine vidéo, je vous montrerai comment faire la même chose (à peu près) avec PyQt et Tkinter, pour vous permettre de juger par vous-mêmes des ressemblances et différences.

Dans le monde wxPython, ce que vous connaissez comme une fenêtre s'appelle Frame. Le terme Window représente n'importe quel élément graphique. Frame, qui signifie cadre, c’est pour une fenêtre principale, qui a un cadre.

Pour lancer ma fenêtre principale dans WxPython, j’ai besoin d'un environnement minimal qui fait un peu la liaison avec le gestionnaire de fenêtres.

Pour écrire un programme WxPython, la première chose à faire est d'importer le module wx . J'utilise exprès la forme «import wx » qui me forcera à préfixer par wx. toute référence à un objet du module. Je définis une classe pour ma fenêtre principale, que je dérive, en utilisant l'héritage objet, de la classe Frame du module wx . Je reviendrai tout à l'heure (et je continuerai dans les prochaines Pythonneries) sur le contenu de la classe MaFenetre. Avec WxPython j'ai aussi besoin d'une autre classe, dérivée de la classe App , pour « Application », du module wx . A l'intérieur, une méthode spéciale qui s'appelle OnInit , qui se lancera quand on créera un objet de la classe. J'y créerai un objet de la classe MaFenetre , j'appellerai la méthode Show (je n'ai pas besoin de l’écrire, elle est héritée de Frame ) pour dire d'afficher la fenêtre, et je retourne True .

Quand je lance mon programme, je commence par créer un objet de la classe MonAppli , puis je lance une boucle en appelant la méthode MainLoop (boucle principale) dont je ne sortirai qu'en fermant la fenêtre. Voilà. C’est un squelette, qui sera commun à toutes les applications WxPython. En général on en fait un modèle, et on l'utilise chaque fois.

Les environnements graphiques, c'est un peu comme la prestidigitation : on voit des tas de choses apparaître magiquement, mais très souvent elles sont préparées depuis longtemps – et cachées. Certains éléments graphiques sont bien entendu créés en fonction des actions de l'utilisateur, mais une grosse partie est préparée à l'avance. C'est la raison pour laquelle, dans une application graphique la méthode, init est souvent assez conséquente, parce qu'elle prépare tout le reste, avec des éléments qui ne sont souvent pas apparents au départ.

Reprenons le dessin de ce que l'on veut. Dans WxPython du texte uniquement destiné à être affiché est associé à un objet de la classe StaticText . Une zone où l'on tape des choses sera un objet de la classe « text control » (TextCtrl ). Et un bouton, eh bien ce sera un objet de la classe Button . Pour créer un élément graphique, je crée un objet de la classe qui y correspond. Suivant le type d'objet, les paramètres passés pour la création varient. Mais tous les éléments graphiques partagent deux paramètres. Le premier s’appelle parent, c'est ce dans quoi apparaît l'élément. Je rajoute un bouton dans une fenêtre, la fenêtre est le parent du bouton. Pour le système, cela veut dire que si je ferme la fenêtre, tous ses descendants dégagent.

L'autre paramètre est un identifiant numérique, propre à chaque élément, et qui peut servir à le repérer (on verra dans quelques pythonneries comment les utiliser). Parfois on se fiche complètement de la valeur exacte, et l'on demande au système de créer automatiquement des valeurs. Il crée alors des valeurs négatives. Parfois on veut gérer cela manuellement, et l'on attribue soi-même une valeur positive, et c'est à vous de vous assurer que vous n'attribuez pas le même numéro à deux éléments différents. Il y a aussi des valeurs prédéfinies, nous en verrons un exemple.

Nous allons tout de suite voir ces deux éléments dans la création de la fenêtre. Je vais la créer en passant d’abord la fenêtre parente – il n'y en a pas, je passe None , puis un identifiant, je me fiche royalement de ce que cela peut être et je passe wx.ID_ANY pour dire au système « prends ce que tu veux ». Enfin, un troisième paramètre plus spécifique, le titre à donner à la fenêtre. Wx.ID_ANY vaut et peut être remplacé par -1, que l'on voit souvent dans les programmes. J’utiliserai parfois -1 pour l'unique raison que c'est plus court et me permet d'utiliser une police plus grosse. Mais je pense qu'il est plus clair d'utiliser wx.ID_ANY .

Je termine ma méthode, passons maintenant à la classe vraiment intéressante, MaFenetre .

La notion de base, c'est que quand on crée une fenêtre on lui associe ce qui dans le jargon WxPython s’appelle un « sizer », qui dans d’autres environnement graphiques s’appelle « lay-out », ce que je pourrais traduire par « disposition ». C’est une espèce de récipient invisible, dans lequel on va mettre les éléments visibles, et qui va contrôler comment ils seront placés les uns par rapport aux autres : un en dessus de l'autre, à côté les uns des autres, dans des positions relatives précises, etc. Cette disposition se maintiendra si l'utilisateur redimensionne la fenêtre. Ensuite, on attache la disposition à la fenêtre et, dans le cas de WxPython, on appelle une méthode de la fenêtre qui lui dit « adapte-toi à la disposition » ; en d'autres termes, « calcule automatiquement ta taille pour t’ajuster à ce que tu contiens ».

Écrivons donc la classe MaFenetre. Je commence par appeler la méthode init de la classe Frame dont je dérive ma fenêtre. Cela crée une fenêtre « de base » sans rien, avec le titre que je lui ai passé. Je lis mon fichier de questions et de réponses, aucun intérêt particulier, je ne fais que reprendre ce que nous avons fait dans la Pythonnerie n°9.

Ensuite je crée un « sizer », et je vais pour l’instant me limiter à la disposition à la fois la plus simple et la plus utile, le BoxSizer , une bête boîte, à laquelle je passe comme paramètre une constante prédéfinie qui dit que je veux que ma disposition soit verticale.

Je crée un objet StaticText , son parent est « self », autrement dit la fenêtre que je suis en train de créer, je me moque de son identifiant, et je passe en troisième paramètre le texte à afficher, ma consigne puisque pour l'instant j'omets le score. Puis je l'ajoute à la boîte. Je n'ai pas oublié bien sûr d’importer le module random pour tirer une question au hasard, et je rajoute encore un objet StaticText qui contient la partie « question » de ce que m'a retourné la fonction choice. Là encore, dans la boîte. Pour lire la réponse, je crée un objet TextCtrl , dans la boîte. Enfin, je crée et ajoute à la boîte un bouton « OK ». Ici, je mets un identifiant spécial, parce que les boutons OK, on en voit partout. Je pourrais dire wx.ID_ANY et spécifier le texte du bouton, mais en disant simplement wx.ID_OK le programme sait ce qu'il doit afficher. L'avantage, c'est que si je le lance après dans un environnement dans une autre langue que le français, il saura adapter le texte du bouton – et mettra « OK » en anglais, et « OK » en allemand, et … Bon, « OK » n'est peut-être pas le meilleur des exemples, mais il y a d'autres boutons standard pour lesquels c'est davantage utile. Je raccroche ma boite à la fenêtre en lui disant de s'adapter, et je la centre, c'est-à-dire que je lui dis de s'afficher au milieu de l’écran. Au passage, l'orthographe de « Centre() » trahit l'origine britannique de WxPython, des Américains auraient écrit T - E – R et pas T - R – E.

Voilà ce que cela donne. Pas trop mal, sauf que je préfèrerais que la zone d'entrée de la réponse soit à côté de la question, pas en dessous. Évidemment, cela vient de la boîte verticale. Si dans le programme je remplace VERTICAL par HORIZONTAL, j'obtiens cela, plutôt pire que mieux sauf pour la partie question / entrée de la réponse.

Ce qui va nous sauver, c’est que l'on peut mettre des boîtes dans des boîtes. Je vais donc créer une boîte horizontale pour les éléments qui doivent être côte à côte, et la rajouter dans la boîte verticale.

Allons-y. Je crée une boite horizontale boite2, puis je rajoute la question et la zone d’entrée de la réponse à boite2, et plus à boîte. Ensuite, je rajoute boite2 à boîte.

Voilà le travail, sauf que, comme vous pouvez le constater, l'alignement n'est pas terrible, et ça manque un peu d'espace.

Alignement et espace sont spécifiés quand on ajoute les éléments dans la boite. Il y a, entre autres, un paramètre additionnel et facultatif appelé flag que je peux spécifier, et comme j'omets d’autres paramètres je dois le spécifier en disant flag= et la valeur que je lui donne.

Je vais spécifier wx.ALIGN_CENTER , avec une orthographe américaine ce coup ci (un développeur américain a dû chercher à se venger). Je le rajoute pour tout ce que je mets dans la boite verticale. Ce n’est pas la peine pour ce que je mets dans la boite horizontale, puisque c'est cette boite que je centre, pas ce que je mets dedans.

Mieux, sauf que si vous avez une petite tendance à la claustrophobie, cela manque encore un peu d'espace.

En fait, quand j'ajoute un élément, je peux spécifier un espace autour. Je vais rajouter à flag une barre verticale, qui signifie une combinaison de caractéristiques, et wx.ALL qui signifie que je veux de l’espace tout autour. Je pourrais demander de l'espace seulement en haut, ou en bas et à droite, avec des paramètres plus spécifiques combinés avec une barre verticale. Un autre paramètre facultatif, border, dit quelle est la taille en pixels de cet espace. Je vais donc rajouter de l'espace autour de mes éléments, un peu plus autour du bouton.

Je vais quand même faire quelque chose dans ma boîte horizontale, qui est une autre manière de donner de l'air, je vais ajouter ce qui s’appelle un « spacer », autrement dit du vide, en spécifiant la taille.

Voilà le résultat final. Évidemment, pour l'instant quand on clique sur le bouton cela ne fait rien, mais nous nous en occuperons dans la prochaine Pythonnerie.


Vidéo Réagir aux événements

Réagir aux événements

Transcript Vidéo

Placer des éléments sur sa fenêtre c'est bien beau, mais pour le moment la fenêtre... ne fait rien.

Dans ce chapitre, nous allons découvrir la notion d'événement. Lorsqu'on cliquera sur un bouton, ou au survol de la souris, on pourra faire en sorte qu'une action soit déclenchée : ouverture d'une autre fenêtre, chargement d'un fichier, arrêt du programme...

Vidéo

Réagir aux événements Transcript

http://www.dailymotion.com/swf/video/xfeykr_tech


Réagir aux événements Transcript

Transcript

Vidéo Gérer les erreurs

Bonjour,

Nous avons vu comment créer une fenêtre qui a à peu près la tête que nous voulons avec wxPython. Comme promis, je vais vous montrer comment on aurait pu la construire avec tout d'abord PyQt, puis Tkinter. En quelque sorte, l'équivalent informatique de la pierre de Rosette qui a permis à Champollion de comprendre les hiéroglyphes.

PyQt se télécharge depuis un site anglais, riverbankcomputing.co.uk. Attention, quand vous le téléchargez, encore une fois, à bien installer la version qui correspond à votre version de Python. Les numéros de version des environnements graphiques ne sont pas identiques aux numéros de version de Python.

Nous avons vu que pour lancer une application wxPython il y avait toute une gymnastique préparatoire avec création d'une application qui lançait la fenêtre principale. Qu'en est-il avec PyQt ? Bien évidemment, on n'importe plus wx, mais on va à la place importer QtGui du module PyQt4. G-U-I, qui se dit gouï, est un acronyme pour Graphical User Interface, interface utilisateur graphique. Le module contient d’autres parties mais pour ce que nous allons faire nous n’avons besoin que de celle là.

Nous avons aussi besoin d'importer un module standard, sys , qui est utilisé en particulier pour passer et récupérer des paramètres sur la ligne de commande.
Notre classe principale n'hérite plus de quelque chose qui s'appelle Frame , mais de quelque chose d'équivalent baptisé QDialog . Nous allons voir tout de suite comment l'écrire. Pour le lancement, c'est plus simple. Plus besoin d'une classe intermédiaire ; On crée directement un objet de la classe QApplication , auquel on passe d'éventuels arguments passés sur la ligne de commande. On crée la fenêtre en invoquant la classe MaFenetre avec un seul paramètre, le titre de la fenêtre. Alors qu’avec wx on associait explicitement dans la méthode OnInit la fenêtre à l'application, ici l'association est implicite. Et derrière on appelle la méthode (héritée de QDialog) exec souligné, qui est l’équivalent du MainLoop pour l’application de wx. Comme vous voyez, assez différent dans la forme, mais pas si différent que cela sur le fond.

Voyons la classe MaFenetre maintenant. Comme avec wx, la première chose à faire est d’appeler la méthode init de la classe dont on dérive. Ici le titre doit être spécifié par appel à une méthode setWindowTitle qui est héritée, si vous voulez tout savoir, de QWidget , classe parente de QDialog . Les conventions de nommage sont légèrement différentes de wx, les noms de méthodes commencent par une minuscule.

Toujours le chargement du fichier de questions, et la boite verticale s’appelle ici QVBoxLayout , V pour Vertical, donc pas besoin de spécifier l'orientation avec un paramètre.

Le texte à afficher est associé à un objet QLabel , qui prend ce texte comme paramètre – pas besoin de spécifier la fenêtre parente ni un identifiant. Pour l'ajouter à la boîte, c’est la méthode addWidget() , « widget » est un terme générique, contraction de « window » et de « gadget », pour désigner un élément graphique. Pour la boîte horizontale, vous auriez pu deviner tout seul que c'est un objet QHBoxLayout , je récupère un couple question deux points réponse, un nouveau Qlabel pour la question, le TextCtrl de wx devient un QLineEdit , et, attention, quand on ajoute la boite horizontale dans la boite verticale, contrairement à wx, on doit utiliser une méthode différente suivant que l'on ajoute un élément ou une boite. Ici, il faut ajouter addLayout() , je raccroche ma boite verticale à ma fenêtre et j’ai fini.

Vous voyez qu'ici centrage - à la fois de la fenêtre et des éléments - et espacement sont automatiques. En revanche, par défaut, le bouton prend toute la largeur de la fenêtre. Je pourrais le retailler, mais alors le centrer me demanderait un peu de travail, et comme je trouve cela insupportable, conformément à ma philosophie minimaliste je m'arrêterai là dans les embellissements.

Passons au troisième mousquetaire, Tkinter . Rien à installer, il est livré avec Python. Comme précédemment on n'importe plus wx mais Tkinter , et la fenêtre principale dérive encore une fois d'une Frame , mais cette fois-ci du module Tkinter . Le lancement est on ne peut plus simple, pas de classe intermédiaire ni d’application, on crée un objet de la classe correspondant à la fenêtre principale en lui passant le titre en paramètre, et on appelle sa méthode mainloop() , cette fois-ci sans « CamelCaps » - toute en minuscules.

Passons à la méthode init de la classe MaFenetre. Comme d’habitude, on commence par appeler la méthode init de la classe parente, comme avec PyQt on a une méthode spéciale pour définir le titre. Lecture du fichier, et là, différence par rapport aux deux autres, je ne spécifie pas de disposition. En fait, on pourrait dire que la « Frame » de Tkinter vient avec une disposition incluse. Ma consigne est un objet de classe Label, et pour l'ajouter à ma fenêtre j'utilise la méthode pack .

Contrairement aux cas précédents, ce n’est pas une méthode du contenant, mais une méthode du contenu – c'est la méthode pack de l'élément graphique que j'appelle, et il est automatiquement rajouté au dernier contenant rencontré. Je peux passer des paramètres padx et pady, pour spécifier l’espace autour horizontalement (padx) ou verticalement (pady).Ici, je demande une marge de 10 pixels au-dessus et au dessous de mon texte. Je récupère une question et une réponse, et dans ce cas ci plutôt que de créer une disposition invisible, je crée une nouvelle « Frame » qui sera, en fait, sans cadre, et que j’appelle boiteh – c’est elle qui me servira à disposer mes éléments horizontalement. Le positionnement horizontal n’est pas une caractéristique de boiteh, c’est en jouant sur pack() que je vais placer mes éléments. En fait, pack a un paramètre, side, qui dit si mon élément va aller en haut du conteneur, en bas, à droite ou à gauche. Je place la question à gauche de boiteh. Vous remarquerez que je ne dis pas que la question va dans boiteh, c’est implicite. Pour la zone d’entrée de la réponse, qui dans ce cas correspond à un objet de la classe Entry , je la mets à droite, puis je rajoute boiteh à ma fenêtre principale avec 5 pixels en dessus et 5 pixels en dessous. Je n’ai plus que mon bouton à mettre, pour lequel je spécifie une marge verticale et une marge horizontale différentes, puis j'appelle self.pack() pour, si j'ose dire, empaqueter le tout. Voilà le travail. Vous remarquerez que la fenêtre apparaît un peu n'importe où sur l’écran. On peut centrer une fenêtre avec Tkinter, mais c'est assez calculatoire (il faut récupérer la taille de l’écran) et un peu laborieux. J'en ferai donc l'économie.

Voilà pour vous donner un petit goût de PyQt et Tkinter. Vous voyez, c’est à la fois très différent et très proche. Je vais maintenant retourner à wxPython.

Pour l'instant, quand on entre quelque chose et que l'on clique sur le bouton, cela ne fait rien. Il y a une bonne raison à cela : on n'a pas dit de faire quelque chose. Comme je le disais dans la Pythonnerie précédente, dans un environnement graphique on est en programmation événementielle : une action de l'utilisateur est un évènement, et il faut associer à cet événement une action. De ce que l'on a vu jusqu'à présent, ce qui ressemble le plus à la programmation événementielle, c'est le « try except » quand on anticipe une erreur. L'exception, c'est un genre d'événement. La différence, c'est qu'avec une exception, si elle se produit et qu'on ne la traite pas, le programme se terminera avec une erreur. Avec un évènement dans l'environnement graphique, s'il se produit et qu'on ne le traite pas, il sera simplement ignoré.

Comme les exceptions, les évènements ont des noms. Quand on appuie sur un bouton, cela produit un événement qui s'appelle, dans wxPython, wx.EVT_BUTTON , ce qui est assez clair. Je vais dire que pour ma fenêtre, j’attache (c'est la signification de « Bind ») à l’événement EVT_BUTTON une méthode verifier_reference qui sera le gestionnaire de l’événement quand l'événement est produit par quelqu’un qui a cliqué sur l’objet bouton.

Il me faut définir maintenant ce que fera ma méthode verifier_reponse . La première question à se poser, c’est de quelles informations a-t-elle besoin ? Tout d'abord, elle doit récupérer la valeur entrée par l’utilisateur dans reponse_utilisateur . Cela veut dire que reponse_utilisateur doit être visible par la méthode verifier_reponse , autrement dit que ce doit être un attribut de la classe MaFenetre, partagé entre init et verifier_reponse . Dans init , partout où j’utilise reponse_utilisateur je dois donc préfixer l'objet par self. .

Mais je veux aussi pouvoir comparer cette réponse à la bonne réponse, qui est dans question_reponse . Même chose donc, dans init je dois partout rajouter self point devant question_reponse .

Je vais aussi calculer un score. Je dois donc comptabiliser le nombre de questions, et le nombre de bonnes réponses. Je vais créer deux attributs pour cela, que je mets au niveau de l'en-tête de la classe. Je vous rappelle que cela revient au même que de les préfixer par self à la première référence. Initialement, mes deux variables contiendront zéro.

Du coup il est peut-être temps que je me préoccupe enfin d'afficher le score dans init . Et si verifier_reponse le change, il faut aussi que ce soit un attribut, donc préfixé par self. . C'est facile, juste un objet de la classe StaticText que je rajoute comme premier élément de ma boîte verticale avec les paramètres de centrage et d'espacement qui vont bien.

Une petite parenthèse importante. Dans la Pythonnerie n°8 je vous avais montré comment enlever les accents de texte pour les ignorer dans la comparaison, avec des tables de conversion construites avec la méthode maketrans . Il y a un problème en environnement graphique, c'est que ce que l'on va récupérer ce n'est pas du texte simple, c’est ce qu'on appelle de l'unicode. L'unicode, c'est un codage éventuellement sur plusieurs octets, beaucoup plus sophistiqué que le codage classique, qui permet de représenter la plupart des grands systèmes d'écriture utilisés dans le monde, y compris certains assez exotiques (vous me direz, on est toujours l'exotique de quelqu'un …). En python, du texte unicode se représente comme du texte « classique », sauf que l’on met un « u » devant le premier guillemet ou la première apostrophe. Et la méthode translate que nous avions utilisé ne fonctionne pas avec l'unicode.

Je vais donc devoir recourir à la magie. Je ne vais pas rentrer dans les détails (même si je l'espère vous devez être capable de comprendre vaguement ce que fait la fonction), recopiez cela, sauvez le sous le nom normalisation.py, voilà une fonction qui enlève les accents pour du texte unicode.

Je vous la montre en action. Une variable unicode pleine d'accents, et hop ils sont partis.
Je voudrais tout de même attirer votre attention sur le fait que si cela fonctionne bien pour les caractères français, ce n'est pas le cas avec toutes les langues. Si vous prenez du danois par exemple (ne me demandez pas ce que cela veut dire, j'ai fait du copié/collé depuis un site danois), vous voyez que le o barré survit au traitement et j'entends d'ici vos hurlements quand vous dites « l'équivalent du A avec un petit rond au dessus c'est A-A, pas A tout seul ». Tout à fait exact. On aurait des problèmes similaires en allemand, où si l'on supprime un umlaut sur un u on s’attend à le remplacer par u-e. Mais je fais simple, et si vous le mettez dans un module cela vous permet de réviser la fonction et de l’améliorer si nécessaire.

Fin de la parenthèse.

Une fois que l'on a vérifié la réponse et que l'on a comptabilisé les points, que fait-on ? Eh bien il va falloir afficher une nouvelle question. Cela veut dire que la liste des questions lue depuis le fichier devra, elle aussi, être un attribut de la classe, et que je dois aussi la préfixer par self. partout où je la rencontre. Même chose pour l'objet qui affiche la question.

Bon, eh bien après tout ça tout ce dont ma méthode verifier_reponse a besoin devrait être accessible. Une méthode gestionnaire d'événement prend nécessairement un paramètre (je ne compte pas self , bien entendu) l'évènement. L'évènement est un objet qui a des méthodes qui permettent de savoir ce qui l'a émis, etc., nous verrons cela plus tard. Première chose, je récupère grâce à la méthode GetValue() le contenu de reponse_utilisateur et je le mets en minuscule pour standardiser. Je vous rappelle que ce que j'obtiens, c’est de l'unicode.

Ensuite, et là c'est semblable au programme de la Pythonnerie n°8, je récupère la réponse officielle, que je mets également en minuscules, et comme je l'ai lue dans un fichier texte on ne peut plus standard, ce n’est pas de l'unicode, c’est du texte quelconque.

Ensuite je compare en enlevant les accents, je suppose ici qu'il n’y a pas d'accent dans le fichier, et je transforme au passage en unicode le texte normal. Dans ce sens là cela se passe bien, parce que, l'unicode, c'est une extension, un peu comme les nombres réels sont une extension des nombres entiers relatifs par exemple. S'il y a des accents dans le fichier, il faut que je les enlève et que je dise à la fonction unicode comment ils sont codés, par exemple il faudrait écrire ceci sous Windows.

Si la réponse est bonne je rajoute un à mon compteur des bonnes réponses. Dans tous les cas je rajoute un à mon compteur des questions. Je vous montre là une syntaxe condensée assez utilisée, qui est équivalente à ce qu'il y a au dessus. Pour rajouter 1 à une variable a, on peut aussi bien dire a += 1 que a = a + 1.

On affiche le score avec la méthode SetLabel , qui permet de changer le texte d'un objet de la classe StaticText (d'autres classes également).

Je récupère avec ma fonction choice la question suivante, et je l’affiche encore une fois avec SetLabel .

Puis j’efface avec la méthode Clear la réponse juste entrée par l’utilisateur.

Voilà, c’est fini, et voilà ce que cela donne.

Un peu rustique, mais fonctionnel. Nous améliorerons ce programme dans les prochains épisodes.


Vidéo Gérer les erreurs

Gérer les erreurs

Transcript Vidéo

Dans ce chapitre, nous allons étudier ensemble les problèmes d'affichage que nous pouvons rencontrer et trouver un moyen de gérer les erreurs qui surviennent lors de l'exécution du programme.

Vidéo

Gérer les erreurs Transcript

http://www.dailymotion.com/swf/video/xfsvas_tech


Gérer les erreurs Transcript

Transcript

Vidéo Modifier l'apparence

Bonjour,

Comme promis, nous allons maintenant améliorer le programme de la Pythonnerie précédente. Cette amélioration va se faire en plusieurs temps. Nous allons tout d'abord corriger des problèmes visuels, améliorer un tout petit peu l'ergonomie, réorganiser le code et rendre le programme davantage robuste.

En fait, on peut avoir quelques soucis avec le programme de quizz tel qu'il est écrit à présent. Pour les mettre en évidence, j'ai ajouté à ma liste d'auteurs Edgar Poe, et François-René de Chateaubriand. Et j'ai légèrement trafiqué le programme pour que le tirage des questions ne soit plus aléatoire.

Supposons donc que le premier nom à surgir soit celui de Poe. Je rentre son prénom, et malheur, quand Chateaubriand apparaît – et bien heureusement que je sais que c'est Chateaubriand. Le problème est facile à comprendre : je vous ai dit que quand, dans la méthode init , j'appelais la méthode SetSizerAndFit , la fenêtre ajustait automatiquement sa taille au contenu. Et le contenu initial était un nom très court. Du coup, la fenêtre se trouve sous-dimensionnée dès que l'on a un nom ; même de longueur moyenne.

Je pourrais imaginer appeler des méthodes (il y en a) pour adapter la taille de la fenêtre à chaque question. Le problème est que la fenêtre passerait son temps à se redimensionner, ce qui ne serait pas très plaisant à utiliser. Il y a une autre solution. Quand je charge mon fichier, je lis toutes les questions et toutes les réponses. Je peux repérer à ce moment là les valeurs les plus longues, et essayer de dimensionner les éléments graphiques dès le départ pour que ces valeurs puissent y tenir sans déborder.

Mais qu’est-ce que c'est la valeur la plus longue ? Nous sommes en environnement graphique, et en environnement graphique on travaille généralement avec des polices variables. J'ai ici 10 i, qui occupent une certaine largeur. Il ne me faut pas beaucoup de m pour avoir une suite de caractères qui occupe davantage de place. Compter le nombre de caractères ne pourra donc donner qu'une idée approximative de la taille. Mais il y a plus ennuyeux. Connaissant le nombre de caractères, comment en déduire une taille qui dans un environnement graphique se calculera en pixels ? Parce qu'en fait, la largeur réellement occupée, c'est avant tout une question de police.

Ce que je vous propose pour régler cette question est un mécanisme relativement simple. Tout d'abord, à la lecture de mon fichier, je vais identifier les longueurs les plus grandes, tant pour les questions que pour les réponses. Je vais me constituer un pseudo-texte de cette longueur maximale, uniquement constitué de x – une lettre de taille moyenne (si j’étais paranoïaque je pourrais employer m ou w – ou je pourrais raffiner en mettant une majuscule. Après, c’est une question d’ajustement). Puis, et je vais faire tout cela pendant que je suis toujours à m'activer en coulisses, avant même de montrer quoi que ce soit à l'écran, je vais mettre ce pseudo-texte représentant ce que je peux avoir de plus long dans un élément graphique ; je vais ensuite utiliser une méthode qui me permet de connaître la taille d'un élément graphique, puis une seconde méthode qui permet de fixer la taille. Je vais donc fixer la taille au plus grand, et quand je mettrai dans l’élément une « vraie » valeur le tour sera joué.

Je vais donc, au début de ma méthode de chargement, mettre à zéro la plus grande longueur rencontrée pour les questions et pour les réponses. Je vais lire mon fichier, et quand j’ajoute quelque chose à ma liste de questions, je contrôle la longueur de la question, et si elle est plus grande que la plus grande valeur rencontrée jusqu’à présent, je la mémorise à la place. Je fais la même chose avec les réponses.

A la fin, je mets à la place de la question et de la réponse avec la méthode SetLabel le nombre de x qui va bien (je vous rappelle que multiplier une lettre par un nombre revient à la répéter ce nombre de fois). Puis j'appelle la méthode de redimensionnement que nous allons voir tout de suite. J'attire tout de même votre attention sur le fait que, du coup, la zone de réponse a elle aussi besoin de devenir un attribut de la classe.

Voyons donc cette méthode de redimensionnement. Les éléments graphiques ont une méthode GetSizeTuple() qui retourne deux valeurs à la fois - une largeur et une hauteur - dans cet ordre. Pour récupérer deux valeurs à la fois, on écrit comme ici variable1 virgule variable2 égale – appel de la méthode. La méthode SetSizeHints permet de spécifier la taille que l'on veut, en précisant des valeurs minimales et des valeurs maximales. Ici je mets les mêmes valeurs dans les deux cas. Après cela, je recalcule la taille des boîtes en appelant leur méthode LayOut - mise-en-page, si vous voulez. Évidemment, du coup, ces deux boites doivent être des attributs de la classe pour que je puisse les voir dans cette méthode. Puis j'appelle la méthode Fit() de la fenêtre principale pour ajuster la taille globale de la fenêtre. Si vous vous rappelez, dans les pythonneries précédentes, j'appelais SetSizerAndFit qui faisait deux opérations à la fois – accrocher la disposition, puis ajuster la taille. Ici, la disposition est déjà raccrochée, donc je n'appelle qu’une méthode Fit() , je recentre parce qu'avec tout ça j'ai toutes les chances d'être décalé, et enfin je déclenche l’événement comme quoi la taille a changé. C'est le même événement que l'on déclenche quand on redimensionne la fenêtre. En fait, c'est surtout pour le gestionnaire de fenêtre, qui fera par exemple se redessiner ce qui était caché par la fenêtre avant et ne l'est plus maintenant si sa taille a réduit. Et finalement, j’appelle la méthode Refresh() qui redessine, dans la fenêtre même, ce qui a besoin d'être redessiné.

Quand ma fenêtre est dimensionnée pour que même la question la plus longue y tienne, je pourrai tirer une nouvelle question et cela va être la fonction de la méthode nouvelle_question qui fait ce que nous connaissons déjà, à savoir piocher une nouvelle question, j'incrémente ici mon compteur de questions, c'est plus logique, je la mets dans l'objet graphique question, puis j'efface la réponse précédente de l’utilisateur. Je vais quand même ajouter une touche nouvelle, l'appel à la méthode SetFocus() de l'objet réponse utilisateur. Cette méthode rend cet objet actif et y positionne le curseur. Elle a le même effet que si l'utilisateur avait cliqué dans la zone d’entrée pour pouvoir fournir sa réponse.

Vous comprendrez, je pense, que l'introduction de ces méthodes va demander une certaine refonte de la méthode init . Les conseils que donnait aux apprentis poètes, il y a de cela plus de trois siècles, Nicolas Boileau dans son « Art Poétique » sont toujours en grande partie valables pour les développeurs informatiques. Et s'il ne faut pas tomber dans le perfectionnisme, il faut construire sur des bases saines qui permettront de faire évoluer le programme. Je vais donc, rapidement, restructurer la méthode init . Tout d'abord, les attributs qui ne correspondent pas à des éléments graphiques, les compteurs et le tableau des questions. Ensuite les éléments graphiques, dont le seul qui n'ait pas besoin d'être vu par une autre méthode est le bouton OK.

Ensuite, mes dispositions qui doivent être des attributs puisqu'on y fait référence dans la méthode redimensionne. Ensuite, je range tous mes éléments qui dans la boite verticale, qui dans la boite horizontale, la boite horizontale dans la verticale, pour les disposer comme je veux. Après, j'accroche l'action verifier_reponse au bouton, et je vais apporter une légère amélioration en rattachant la même action à l’évènement EVT_TEXT_ENTER quand il est émis par l'objet reponse_utilisateur. Qu'est-ce que c'est que c’est événement ? C'est ce que produit le fait d'appuyer sur la touche « Entrée » quand on a tapé la réponse. En d’autres termes, appuyer sur « Entrée » aura le même effet que cliquer sur OK.
Ensuite une légère modification : j’attache la boite verticale à la fenêtre, mais uniquement avec SetSizer() , pas SetSizerAndFit() , puisque l'ajustement de la taille se fera après avoir chargé les données.
Et au final, je charge le fichier questions.txt puis je tire ma première question.

Pour terminer avec la restructuration du code je peux maintenant simplifier verifier_reponse en appelant nouvelle_question après avoir vérifié et changé le score. Le comptage des questions, rappel, se fait dans nouvelle_question.

Voilà, le code est plus propre et prêt à supporter des évolutions. Mais ne peut-on rendre le programme plus robuste ? Si vous vous rappelez la Pythonnerie n°9, j’avais introduit les exceptions dans le cadre d’un fichier de questions absent. Ce n’est pas parce que l'on est en environnement graphique que les mêmes problèmes ne se produiront pas ! Voyons ce qui se passe si je renomme mon fichier. Si sous Windows je double-clique sur le .pyw, cela va donner à peu près cela... Bon, je lis vite, mais il y a des limites. En fait, si je lance le programme depuis Idle, la fenêtre va rester affichée un peu plus longtemps, et voici ce qu’elle contient. On retrouve le même genre de message mystérieux qu’en mode console, dans une fenêtre qui s’ouvre pour la circonstance.

Nous allons, comme précédemment, attraper l’exception au vol, mais mettre le message dans une fenêtre comme vous en avez probablement déjà vu beaucoup, une fenêtre qui demande de cliquer sur un bouton pour continuer.

Je vais donc commencer par modifier ma méthode chargement et mettre dans un bloc try la lecture du fichier et tout ce qui en découle.

Attaquons nous à la partie intéressante, la partie except dans laquelle je n’attrape que les erreurs auxquelles je m’attends, à savoir IOError, dont je récupère le numéro de message, que je n’utiliserai pas, et le texte du message d’erreur.

Je vais créer un objet de la classe MessageDialog , qui est sensiblement différent des autres éléments graphiques sur le plan des paramètres qu’il attend à la création. Toujours self pour indiquer la fenêtre parente, mais ensuite le texte qui doit apparaître dans le fenêtre, le titre à lui donner dans le bandeau, puis le paramètre don le nom est « style » et qui spécifie ce à quoi ressemblera la fenêtre.

J’appelle ensuite la méthode ShowModal() de l’objet que je viens de créer, ce qui a pour effet de l'afficher. On parle de « fenêtre modale » quand on a une fenêtre qui bloque tout le reste tant que l'on n'a pas cliqué sur ses boutons ou qu'on ne l'a pas fermée. Tant qu’elle est active, le reste de l'application est désactivé.

Cette méthode retourne soit wx.ID_OK (à ne pas confondre avec wx.OK , qui est le style) si je clique sur le bouton, soit wx.ID_CANCEL si je la ferme sans cliquer sur le bouton. Dans la mesure où tout ce qui m'importe c’est que l'utilisateur ait vu le message, inutile de vérifier cette réponse.

Un petit panorama très rapide des boîtes de message. Ce que nous venons juste de voir, c'est la boîte d'erreur classique : le texte, le titre, le bouton, et l'icône facultative dont l'apparence peut varier suivant le système sur lequel l’application tourne. Ce genre de boîte est purement informatif (il y a également une icône wx.ICON_INFORMATION si vous voulez être moins alarmant).

On peut également avoir une boîte de message pour demander une décision à l'utilisateur – le message, le titre, un style wx.YES_NO correspondant à deux boutons, combiné à une icône interrogative. Deux choses importantes à remarquer : d'une part, le bouton de fermeture de la fenêtre est désactivé. Ce sera oui ou non, il n'y a pas d'échappatoire possible. L'autre chose, c'est que, bien entendu, il me faut récupérer cette fois ce sur quoi l'utilisateur aura cliqué, et j'obtiendrai wx.ID_YES ou wx.ID_NO suivant le cas.

Enfin, je peux aussi avoir les deux boutons (ou simplement un bouton OK) combinés avec wx.CANCEL pour ajouter un bouton d'annulation en cas de fausse manœuvre. Une icône différente, et surtout ce coup-ci je peux fermer la fenêtre sans cliquer sur un bouton, ce qui sera identique en fait à cliquer sur le bouton d’annulation et retournera wx.ID_CANCEL . Il va sans dire que là aussi je veux contrôler la réponse.

Retournons à notre méthode chargement. Quand je clique sur OK, la fenêtre disparaît mais en fait elle est toujours là, invisible – elle ne peut pas disparaître tout à fait et retourner une valeur. C’est la raison pour laquelle il faut la détruire explicitement en appelant sa méthode Destroy() . Après quoi, très important, je lance de nouveau l'exception. Pourquoi cela ? Eh bien, normalement, après le chargement, je fais plein de choses, à commencer par tirer une question, ce qui, évidemment, risque de s’avérer problématique si je n'ai pas trouvé le fichier. Comme j'ai attrapé l’exception, pour Python cela devient en quelque sorte une affaire classée et pour lui tout va bien quand je retourne de la méthode. Relancer l’exception est ma manière de dire à la méthode init que tout n'a pas marché comme sur des roulettes.

Du coup je modifie init , je mets chargement et ce qui suit dans un bloc try , et si j'ai une exception, sachant que l’utilisateur a vu le message d’erreur, la fenêtre se détruit elle-même et l’application se termine. Nous n’en avons pas encore terminé avec ce programme, mais ce sera pour la prochaine fois.


Vidéo Modifier l'apparence

Modifier l'apparence

Transcript Vidéo

Vous voudriez maintenant changer l'apparence de votre programme ? Nous allons découvrir tout ce que nous pouvons faire en Python pour modifier les couleurs, les images et icônes de nos programmes. :)

Vidéo

Modifier l'apparence Transcript

http://www.dailymotion.com/swf/video/xfsvib_tech

Télécharger le code source du programme de Quizz


Modifier l'apparence Transcript

Transcript

Vidéo Boutons, menus et fichiers

Bonjour,
Je vais continuer avec le programme de quizz, maintenant plus solide mais, il faut bien le dire, pas terriblement attrayant. Je vais donc, dans cette Pythonnerie me concentrer exclusivement sur des aspects visuels et ergonomiques, ce qui permettra d'introduire quelques classes et méthodes intéressantes, et de discuter de principes non moins intéressants.

Pour rendre visuellement le programme moins austère, je vais rajouter des smileys à côté du score, ce qui sera plus sympa que des chiffres tout seuls. Je mettrai un smiley attristé quand la réponse sera mauvaise, et un smiley enthousiaste quand elle sera bonne. Je suis allé chercher mes smileys sur Wikicommons où il y en a plein qui ont le bon goût d'être dans le domaine public et de ne pas poser de problème de copyright, je les ai réduits avec Gimp, mon programme favori de traitement d'images, puis je les ai sauvegardés au format PNG sous les noms rate.png, bof.png – un smiley neutre -, bien.png – celui là, je ne l'utiliserai pas dans cette Pythonnerie mais je me le garde pour la prochaine – et super.png. J'ai mis les fichiers dans le même répertoire que mon fichier .pyw.

Première chose, si je veux un smiley A CÔTÉ du score, cela veut forcément dire une boîte horizontale supplémentaire. Gardons cela à l'esprit.
Autre chose importante, quand vous avez un fichier graphique, que ce soit un .jpg, un .gif, un .png, c'est un format compressé ; c'est-à-dire que l'image brute a été traitée et encodée pour obtenir, sans trop diminuer la qualité de l'image, un fichier le plus petit possible qui se transférera relativement vite sur internet par exemple. Vous savez que quand vous avez une image sur votre écran, c'est en fait, quand on l'agrandit suffisamment, une foultitude de points - les pixels - avec chacun sa couleur. L'ensemble des points forme ce qu'on appelle un bitmap. Dans un format compressé, on va utiliser des astuces – par exemple, si l'on a n points rouges qui se suivent, le fichier pourrait stocker « n points rouges » au lieu de répéter « point rouge » n fois (les vrais compressions sont beaucoup plus complexes, c'est juste pour donner un exemple). Dans mon programme graphique, je ne peux pas balancer à l'écran directement ce que je trouve dans mon fichier, il faut que je le décompresse pour le transformer en bitmap affichable.

Je vais donc me créer pour chacun de mes smileys un bitmap, ce que je fais en créant pour chacun d'eux un objet Image, auquel je passe en paramètre le nom du fichier (on peut aussi spécifier le format d'image, mais en général python est assez intelligent pour le déterminer tout seul). Cet objet image chargera le fichier, mais derrière je dois créer un bitmap avec BitMapFromImage . Vous remarquerez que je préfixe le nom de mes images par self point, puisque c'est évidemment dans la méthode qui vérifie les réponses que je déciderai laquelle afficher.

Un point important à bien comprendre c'est qu'à ce stade mes images ne SONT PAS des éléments graphiques au sens d'un objet StaticText par exemple. En fait, ce sont des variables de mon programme python qui contiennent un bitmap au même titre que d'autres variables contiennent du texte ou des nombres.

Pour avoir un véritable élément graphique affichable, il me faut créer un objet StaticBitmap qui est le pendant de StaticText, et initialement je vais y mettre le bitmap du smiley neutre que j'ai appelée bmpbof.

N'oublions pas que nous avons besoin d'encore une boite horizontale, que je vais appeler boite3 pour ne pas tout renuméroter même si au final elle apparaîtra avant boite2, l'autre boîte horizontale.

Je modifie ma disposition en mettant d'abord l'icône dans boite3, puis le score, puis boite3 dans boite avant d'ajouter dans boite la consigne, avec ce qu'il faut pour l'alignement et l'espacement comme d'habitude.

Pour la méthode init , c'est fini.

Dans la méthode verifier_reponse, c'est ultrasimple : tout comme la méthode SetLabel de StaticText me permet de changer le score au fur et à mesure, j'ai avec un objet StaticBitmap une méthode SetBitmap qui me permet de remplacer le smiley affiché par un smiley enthousiaste quand la réponse est bonne, et par un smiley attristé quand elle ne l'est pas.

Voyons ce que cela donne à l'exécution : je mets d'abord une réponse correcte, j'appuie sur entrée pour l'envoyer, et j'ai le smiley enthousiaste. Le coup suivant, je mets une réponse erronée, j'appuie sur entrée, et j'ai bien le smiley attristé. C'est quand même plus agréable que le comptage des bonnes réponses tout seul.

Sur notre lancée, j'ai peut-être réveillé chez vous des instincts artistiques et peut-être êtes vous tentés d'améliorer encore le côté visuel, par exemple en remplaçant le gris tristounet de la fenêtre par autre chose.

Vous pouvez facilement changer la couleur du fond de n'importe quel élément en appelant sa méthode SetBackgroundColour avec un paramètre qui est le code de la couleur. Attention à l'orthographe, là encore c'est une orthographe britannique avec colour écrit c-o-l-o-U-r et pas c-o-l-o-r comme le font les Américains. Si vous n'avez jamais joué avec des couleurs sur un ordinateur le code doit vous sembler bien barbare. Le # signifie juste que c'est un code couleur, et les 6 lettres et chiffres qui suivent représentent une quantité de rouge, vert et bleu qui permet d'obtenir toutes les nuances. Je ne vais pas me lancer dans des explications sur la représentation des couleurs, mais ce que je vous conseille si vous n'y connaissez pas encore grand-chose c'est de chercher « couleurs CSS » sur internet. CSS est une espèce de langage déclaratif pour définir l'apparence des pages sur les sites web. CSS utilise soit des noms de couleurs, plus faciles à mémoriser que des codes, soit des codes qui sont exactement les mêmes que ceux que vous utiliserez dans wxPython.

Voilà ce à quoi pourrait ressembler la fenêtre recolorée dans votre environnement. Sauf que... L'apparence, c'est quelque chose qu'un utilisateur peut redéfinir dans une large mesure sur sa machine. J'ai regardé sous Windows XP ce que j'avais comme jeux de couleurs et thèmes, et j'ai découvert « Citrouille », qui donne cela – et là, mon programme de quizz fait un peu tache. Le bleu fait franchement layette. Remarquez bien que cela pourrait être pire. Par exemple, il y a aussi cela, et non seulement cela fait layette, mais en plus comme le texte est en blanc c'est franchement illisible. Ce qui m'amène à vous dire que c'est en général une mauvaise idée que de vouloir trop personnaliser votre application. Une couleur que vous aimez beaucoup peut sembler horrible à quelqu'un d'autre, et comme ici perdre complètement le contraste. Quelqu'un qui travaille en blanc sur fond noir n'a pas forcément trop regardé la famille Adams, cela peut-être pour des raisons de confort visuel ou de sensibilité particulière aux couleurs. A la limite, cela aurait été un moindre mal en forçant AUSSI la couleur de la police à noir (il y a une méthode SetForegroundColour pour cela), vous auriez gardé le contraste mais l'application aurait quand même fait bizarre avec un thème non conventionnel. Je pense donc que le mieux est de conserver le plus possible les valeurs par défaut. Ce que l'on peut faire, en revanche, c'est travailler de manière relative. Je m'explique. Vous pouvez trouver que c'est bizarre d'avoir le fond de la fenêtre foncé et le bouton clair (si vous avez fait attention, ce n'était le cas ni avec Tkinter ni avec PyQt), et vous pouvez vous dire « j'aimerais avoir le fond de la fenêtre clair comme le bouton » (ou l'inverse). Pour faire cela, il existe une méthode qui s'appelle GetDefaultAttributes() qui permet de récupérer pour n'importe quel élément, que ce soit la fenêtre elle-même ou un élément comme le bouton, des choses comme le type et la taille de la police, la couleur de fond ou la couleur de la police, que je retrouve comme attributs de l'objet. Par exemple, la couleur de fond correspond à l'attribut colBg, et je peux dire que le fond de la fenêtre est de la couleur correspondant au colBg du bouton. Voilà ce que cela donne dans un environnement, et si l'on applique le test Citrouille, miracle, la fenêtre se fond miraculeusement dans le thème tout en ayant les caractéristiques demandées. Ce serait la même chose dans un thème gothique. Je crois qu'il est important que vous essayiez d'avoir des applications caméléons, qui se fondent dans le thème choisi par l'utilisateur. Que vous trouviez certains thèmes hideux n'est pas le problème – si vous utilisez judicieusement les valeurs par défaut et laissez tout se régler tout seul, votre application se fondra magiquement dans le paysage.

Dans le même esprit, on peut dire qu'on aimerait que la consigne, qui est quelque chose d'important, soit en gros et que la question soit un peu plus grosse que le standard. Après tout ce que j'ai dit, je ne vais pas fixer la taille de la police et dire que je veux par exemple une taille 16. L'utilisateur peut changer la résolution de son écran, peut-être avoir une taille de police par défaut différente de ce à quoi je m'attends, il peut avoir une mauvaise vue et utiliser une police beaucoup plus grosse que ce que je voudrais mettre en relief. Là encore, je vais tout définir relativement au défaut. L'attribut font de l'objet retourné par GetDefaultAttributes est lui-même un objet, dont la méthode GetPointSize() retourne la taille, et la méthode GetFamily() retourne la famille, c'est-à-dire en gros l'apparence globale des lettres, largeur variable ou pas, empattements ou non. Ces informations vont me permettre de créer deux polices dérivées de la police par défaut, l'une pour les consignes dont la taille sera le double de la taille par défaut, la famille sera la famille par défaut, le troisième paramètre dit si je veux de l'italique ou pas, et le dernier des paramètres obligatoires est ce qu'on appelle en typographie la graisse. Je mettrai la consigne en gras, ce que je spécifie avec wx.BOLD .

Pour la question, la taille sera une fois et demi la taille par défaut, ce qui me force à une conversion en entier au cas où cela ne tomberait pas rond, pour tout le reste ce sera les valeurs standard.

Derrière, je n'ai plus qu'à utiliser la méthode SetFont() pour appliquer police_consigne au texte de la consigne, et police_question à celui de la question, ainsi qu'à celui de la réponse qui n'a pas de raison d'être plus petite.

Voilà ce que cela donne, et comme j'ai pris soin de régler les tailles dans la Pythonnerie précédente sans rien fixer en dur tout s'adapte automatiquement. Nous terminerons notre travail sur ce programme dans la prochaine Pythonnerie, en lui rajoutant deux fonctionnalités importantes.


Vidéo Boutons, menus et fichiers

Boutons, menus et fichiers

Transcript Vidéo

Allons plus loin dans la personnalisation de notre fenêtre ! Ici, nous allons manipuler en détail les boutons, créer nos propres menus et découvrir comment lancer une boîte de dialogue d'ouverture de fichier !

Vidéo

Boutons, menus et fichiers Transcript

http://www.dailymotion.com/swf/video/xfsvy5_tech

Télécharger le code source du programme de Quizz


Boutons, menus et fichiers Transcript

Transcript

Vidéo Les bases de données et le langage SQL

Bonjour,
Je finirai avec ce programme simple qui nous aura quand même bien occupé en lui rajoutant deux fonctionnalités importantes. La toute première était en fait dans la version non-graphique du programme : quand on donnait une réponse fausse, le programme fournissait tout de suite la bonne solution, ce qui permettait non seulement de vérifier ses connaissances, mais aussi de les réviser quand elles n'étaient pas au point.

La solution facile serait de dire : quand l'utilisateur entre une mauvaise réponse, hop, je lui balance à l'écran une boîte de dialogue avec la bonne réponse. On sait faire, on a vu cela avec les erreurs quand on ne parvient pas à trouver le fichier de questions.

Mais je vais essayer de faire plus subtil que cela. Par exemple, j'aimerais pouvoir donner une seconde chance à l'utilisateur. Parfois il n'a pas la moindre idée de la bonne réponse, mais parfois aussi il peut hésiter entre deux possibilités. Si l'on prend deux piliers de la poésie romantique, Lamartine et Vigny, lequel s'appelait Alphonse, et lequel Alfred ? On peut hésiter, et ce n'est pas la même chose que de ne pas savoir du tout. Il y a encore un cas, celui où l'on connaît parfaitement la bonne réponse et l'on fait une faute de frappe ou d'orthographe stupide. On n'a pas toujours envie d'avoir une fenêtre qui vous saute à la figure avec la bonne réponse.

Je vais donc faire autre chose. Je vais ajouter un bouton qui pourra donner la solution, mais je vais commencer par le désactiver. L'utilisateur entre sa réponse, et si elle est mauvaise, j'active le bouton. A ce moment-là, l'utilisateur a le choix : soit il ne connait vraiment pas la bonne réponse et peut la visualiser en cliquant sur le bouton, soit il peut tenter sa chance une autre fois. S'il a bon, il n'aura qu'un demi-point mais c'est plus encourageant que rien du tout. Et s'il a faux au deuxième essai, à ce moment-là, j'affiche la réponse.

Première chose, je vais définir un attribut qui est le nombre de chances restantes. Au début, il vaudra 2.

Ensuite, je vais rajouter mon bouton pour donner la solution. Comme c'est lorsque je vérifie la réponse que je décide si je dois l'activer ou non, ce bouton, contrairement à l'autre, doit être un attribut et je le préfixe par self point. Et ensuite, j'appelle sa méthode Disable() pour le désactiver. C'est un peu ce que je vous expliquais dans la Pythonnerie 18 avec le magicien : tout sera en place, je vais définir les actions associées au bouton dès le départ, mais j'actionne un commutateur pour le rendre inopérant.

Prochaine étape, je vais mettre mes deux boutons l'un à côté de l'autre, donc je commence par créer encore une boîte horizontale,
Puis j'ajoute mes deux boutons à cette boîte avec un peu d'espace entre, et la boîte horizontale dans la boîte verticale. Rien que du classique.
Je définis une méthode, encore à écrire, donner_solution, et je l'associe à l'événement qui correspond à cliquer sur self.bouton2.

Le gros du travail, c'est dans verifier_reponse qu'il va se passer. J'arrive ici quand l'utilisateur propose une réponse, donc je vais lui commencer par lui enlever une chance, puisqu'il vient de brûler une cartouche. Ensuite, je ne lui proposerai de nouvelle question que si sa réponse était correcte ou s'il a épuisé toutes ses chances. Je vais donc utiliser une variable logique question_suivante et la mettre à False pour dire « non, on ne passe pas à la prochaine question ».

Maintenant, au travail. Le smiley enthousiaste, je ne vais le montrer que si l'utilisateur a donné la bonne réponse du premier coup. Je vais donc tester mes chances restantes, et s'il en reste encore une après celle là, je vais ajouter un point et me montrer enthousiaste. Sinon c'est le deuxième essai et je ne vais ajouter qu'un demi-point, le lot de consolation, et faire usage du smiley que je n'avais pas utilisé jusqu'à présent et que j'ai appelé bmpbien – satisfait, mais sans excès.

Dans un cas comme dans l'autre, j'ai la bonne réponse et je dis, en mettant ma variable logique à True, que l'on peut passer à la question suivante.

C'est si l'utilisateur s'est trompé que ça se complique. Le smiley sera toujours le smiley attristé, pas de problème. S'il reste encore une chance après celle-là, on va activer le bouton donnant la solution : l'utilisateur sera libre ou non de voir la réponse ou de tenter sa chance encore une fois. Au bout de deux essais infructueux en revanche, je vais donner la bonne réponse « de force » en appelant la méthode normalement appelée par le bouton. Là je vais faire quelque chose de pas très joli : si vous vous rappelez, une méthode déclenchée par un événement prend toujours un événement comme paramètre. Je vais donc créer un objet « MouseEvent » , évènement souris, et le passer en argument ; j'aurais pu utiliser n'importe quel événement. Comme je vous le disais, c'est de la programmation de fainéant : normalement je ne devrais pas appeler donner_solution ici, mais une méthode qui serait aussi appelée par donner_solution ; j'essaie de limiter le nombre de méthodes pour ne pas trop vous perdre en cours de route.

Si je peux passer à la question suivante, c'est-à-dire si j'ai eu la bonne réponse, j'affiche le score et j'appelle nouvelle_question. Sinon, j'efface la réponse erronée et je remets le curseur dans la zone de réponse.

Une toute petite modification à nouvelle_question au passage : je remets à 2 le nombre de chances, et je désactive le bouton qui donne la réponse parce que je souhaite au moins un essai (ceci dit, une réponse vide est acceptée comme une mauvaise réponse).

Il ne me reste plus que la nouvelle méthode donner_solution à écrire, et comme depuis la Pythonnerie 20 vous êtes des virtuoses de la fenêtre de dialogue, cela va être facile. Récupération de la bonne réponse, une fenêtre de dialogue pour l'afficher avec juste un bouton OK et une icône « information », affichage en mode modal, c'est-à-dire qui désactive le reste de l'application, destruction au retour, je remets le smiley neutre pour ne pas trop culpabiliser l'utilisateur, j'affiche le score, et je passe à la question suivante.

Une petite démonstration rapide de ce que cela donne...

Pour finir, je vais ajouter une fonctionnalité importante à mon programme : la possibilité de changer à la volée de questionnaire. Pour cela, je vais ajouter un classique menu, avec un choix « Fichiers ». Cela fait un peu bête, un choix unique dans un menu (si l'on n'a qu'un choix, il vaut mieux un bouton), je vais donc rajouter un « Aide » à côté pour garnir un peu. Quand je cliquerai sur « Fichiers » j'ouvrirai un sous-menu, avec là encore deux choix, l'inévitable « Quitter », et surtout « Ouvrir » pour changer de questionnaire. Ce que l'on a en haut s'appelle en wxPython « MenuBar » et c'est ce que j'ai appelé sous-menu que wxPython appelle « Menu ». Quand on cliquera sur « Ouvrir », une fenêtre permettra de choisir un nouveau fichier contenant consigne et couples de questions et de réponse. Avant que vous ne blêmissiez en vous demandant comment on va coder tout cela, je vous rassure tout de suite : ouvrir (ou sauver) un fichier est une opération tellement courante qu'il y a, et c'est vrai dans tous les environnements graphiques, un composant tout fait qui s'occupe de tout. En wxPython cet objet s'appelle un FileDialog.

Retroussons nos manches et allons y. Pour créer la barre de menu, on crée un objet MenuBar, jusque là c'est facile. Ensuite, nous allons ajouter des éléments, ce qui se fait de gauche à droite, assez évident quand on est habitué à écrire en français mais qui ne va pas forcément de soi pour tout le monde. Je vais d'abord créer un objet menu pour mon sous-menu, puis lui ajouter deux options avec sa méthode append() . Trois choses sur lesquelles je veux attirer votre attention ici. Tout d'abord, le premier paramètre passé à la méthode est un entier positif arbitraire. Vous vous rappelez j'espère que je vous ai dit qu'il était possible d'identifier les composants graphiques par un entier positif, ou que l'on pouvait laisser Python leur attribuer un identifiant négatif. Les choix dans les menus sont des composants graphiques particuliers, que l'on peut cliquer de manière individuelle, et c'est le même principe. L'identifiant que l'on attribue est arbitraire, la seule contrainte est de ne pas utiliser deux fois le même. Le deuxième paramètre est l'intitulé, et vous pouvez remarquer les « et » commerciaux qui précèdent une lettre que l'on souhaite utiliser comme raccourci clavier. Par exemple, dans le premier cas, appuyer simultanément sur « Alt » et « Q » devrait faire la même chose que cliquer sur la ligne, ou s'y déplacer avec les flèches et appuyer sur « entrée ». Je dis « devrait », parce que cela ne fonctionne pas toujours, cela dépend des environnements. Mais cela ne coûte rien de le prévoir. Il faut évidemment faire attention à n'utiliser comme raccourcis que des lettres distinctes. La dernière chose, c'est l'appel à la méthode AppendSeparator() qui insère simplement une barre horizontale entre deux options sans aucun rapport pour mieux les distinguer.

Ensuite, j'appelle la méthode Append() de la barre des menus, et cette fois-ci le premier paramètre n'est pas un entier mais le menu que je viens de créer. Pour l'intitulé, c'est pareil.

Deuxième sous-menu, où j'appellerai une méthode pour afficher un vague mode d'emploi, et une méthode pour dénoncer l'auteur. Je rajoute ce deuxième menu à la barre de menu sous l'intitulé « Aide ». J'utilise ensuite la méthode SetMenuBar() pour rajouter la barre de menu à la fenêtre principale.

Il ne nous reste plus, comme avec les boutons, qu'à associer une action, c'est-à-dire l'appel d'une méthode, à l'évènement qui consiste à sélectionner une option d'un menu, évènement qui s'appelle EVT_MENU. Toujours la méthode Bind(), mais cette fois-ci j'associe l'identifiant numérique du choix du menu en spécifiant id=. Je suis obligé d'utiliser l'identifiant parce que je n'ai pas de variable qui contienne un objet de type « choix de menu ».

Vous remarquerez que je n'ai pas besoin d'associer d'action aux choix de la barre de menu : quand on leur associe un menu, l'ouverture de celui-ci est l'action automatique.

Il ne mous reste plus qu'à écrire les différentes méthodes de gestion des évènements … La plus complexe est changement_questionnaire. Tout d'abord, je crée un objet FileDialog, en passant en paramètre la fenêtre parente, self, c'est-à-dire la fenêtre principale, Le message correspond au titre qui sera donné à la fenêtre de dialogue. DefaultDir est le répertoire par défaut, et ici je vais utiliser une fonction du module « OS », comme « Operating System », c'est-à-dire système d'exploitation. Il me faudra donc rajouter au début du programme un « import os ». getcwd() veut dire « Get current work directory », en français « obtiens le répertoire de travail courant ». En pratique, cela veut dire que l'on va regarder dans le répertoire où se trouve le programme .pyw. Je précise ensuite avec DefaultFile mon filtre de recherche de fichiers : je recherche des fichiers texte, donc je précise que je ne veux voir que les fichiers .txt et implicitement les répertoires. Le style wx.FD_OPEN dit que je veux ouvrir un fichier. C'est important, parce que pour sauver un fichier j'ouvrirais exactement le même dialogue, sauf que je dirais wx.FD_SAVE.
Je teste le retour de la méthode ShowModal. Là, de deux choses l'une, soit l'utilisateur a effectivement choisi un fichier, soit il a annulé. S'il a annulé, je n'ai rien à faire mais s'il a choisi un fichier la méthode retourne OK, et je récupère la liste des fichiers sélectionnés avec la méthode GetPaths() (certaines applications, vous l'avez peut-être déjà utilisé, vous permettent d'ouvrir simultanément plusieurs fichiers). Dans ce programme on ne peut ouvrir qu'un fichier, je prends donc le premier élément de la liste, l'élément 0, et je le passe à la méthode chargement. Après quoi, je peux détruire mon dialogue, et passer à la première question.

Quelques modifications très mineures à chargement() : quand je charge un nouveau fichier, il faut que je remette tous mes compteurs à zéro. D'abord je vide la liste des questions, en disant que je remplace l'ensemble des éléments par une liste vide. Je suis forcé de faire cela sinon les nouvelles questions se rajouteraient aux anciennes au lieu de les remplacer. Ensuite je mets mes compteurs à zéro et j'affiche le smiley neutre.

Les autres méthodes pour terminer : une fenêtre de dialogue pour les règles du jeu, je pense ne plus avoir besoin de commenter. Pour signer le forfait, c'est un peu spécial. Je crée un objet, une structure en fait, qui s'appelle AboutDialogInfo et est faite pour contenir des informations standard. L'objet a un certain nombre d'attributs que l'on renseigne, le nom du programme, son numéro de version, le copyright – ici ce que je mets veut dire que vous pouvez faire de que vous voulez de ce programme, même le vendre, à condition que je sois mentionné comme auteur, la liste des auteurs, où vous pourrez rajouter votre nom si vous avez de bonnes idées pour améliorer encore ce programme. Pour afficher ces informations, je les passe à la fonction AboutBox(). Pas de ShowModal(), et pas de Destroy(), la fenêtre est détruite automatiquement quand on en sort.
Mais évidemment, ma méthode préférée c'est celle que j'ai appelée ciao() pour terminer l'application...

Et voilà, c'est fini, et vous avez vu sur cette exemple assez simple au départ beaucoup d'objets graphiques qui vous aideront à donner une allure sympa à certains de vos programmes. Comme je vous l'ai dit tout au début, je ne vous ai montré qu'une toute petite partie et il y a beaucoup à explorer dans la documentation. Je vous montrerai bientôt une autre application graphique, avec d'autres caractéristiques. Mais c'est une application de type base de données, et je ferai dans la prochaine Pythonnerie une grosse parenthèse sur les bases de données et le langage SQL.


Vidéo Les bases de données et le langage SQL

Les bases de données et le langage SQL

Transcript Vidéo

A partir de ce chapitre, nous allons travailler avec les bases de données depuis le langage Python !
Mais savez-vous ce que sont les bases de données et à quoi elles servent ? Avez-vous entendu parler du langage SQL ? Non ? Nous allons les découvrir maintenant !

Vidéo

Les bases de données et le langage SQL Transcript

http://www.dailymotion.com/swf/video/xg98b8_tech


Les bases de données et le langage SQL Transcript

Transcript

Vidéo Gestion d'une collection de films en Python et SQL

Bonjour,

Je fais une parenthèse où je ne parlerai pas de Python, mais d'un sujet très important en informatique, les bases de données. Pour mettre les choses en perspective, j'ai cherché quand j'ai commencé à penser à cette présentation combien il y avait de livres sur Python dans la catégorie « langages de programmation » sur amazon.fr. J'en ai trouvé douze. Sur amazon.com, il y en avait 119. J'ai recherché sur « SQL » dans la même catégorie, sur amazon.fr il y avait 312 titres et sur amazon.com … eh bien il y en avait 840. SQL est un langage qui a environ 15 ans de plus que Python, c'est normal qu'il y ait davantage de littérature, mais je pense que cela vous convaincra que c'est un gros morceau.

Du coup, ce que je vais vous présenter ici, c'est vraiment de l'extrait d'essence de concentré. Mais j'essaierai de faire de mon mieux.

Accéder à un fichier en lecture et en écriture dans un programme, on sait faire. Où les choses se compliquent en général, c'est quand deux programmes essaient d'accéder au même fichier en même temps. Le problème s'est posé très tôt en informatique, en particulier dans les systèmes de réservation des compagnies aériennes qui ne voulaient pas vendre le même siège à plusieurs personnes différentes. Très vite, on a pensé à avoir des programmes appelés systèmes de gestion de bases de données, ou SGBD, dont la seule fonction est de s'occuper des données. Un programme émet ce qu'on appelle une requête, et le SGBD s'occupe des accès aux fichiers et retourne les données. Et quand plusieurs programmes l'attaquent en même temps, c'est lui qui coordonne et met éventuellement en attente, c'est transparent pour le programme. L'ennui, c'est qu'au début il fallait quand même que le programmeur ait une bonne idée de la manière dont les données étaient stockées pour écrire ses requêtes. Et là encore très vite s'est posée la qu
estion de l'indépendance logique, c'est-à-dire d'avoir des systèmes où le programmeur n'a absolument pas besoin de savoir comment les données sont stockées. Et de ce côté-là les différentes solutions proposées laissaient beaucoup à désirer, jusque vers 1970 où un chercheur d'origine anglaise d'IBM, Codd, a eu une idée qui a complètement révolutionné les bases de données. Codd était un mathématicien de formation, et si jamais vous lisez un jour son papier original vous vous en rendrez compte. Son idée a été de s'appuyer sur des théories mathématiques existantes et sur la logique.

Imaginez que vous soyez un opérateur de téléphonie mobile. Il y a un certain nombre de choses qui vous intéressent chez vos clients – leur nom, leur adresse, vous voulez leur relevé d'identité bancaire pour prélever les factures, vous gérez leur numéro, vous allez comptabiliser le nombre d'appels locaux, de SMS et d'appels non locaux par exemple. Tout ça définit ce que je veux gérer pour mes abonnés (au moins en partie) et Codd appelle cela une relation – dans cette relation, je stockerai les faits que je connais sur chacun des abonnés. Deux choses très importantes pour Codd – il n'y a pas d'ordre particulier prédéfini, ni parmi les attributs d'un abonné, peu importe si je range son nom avant ou après son adresse, ni entre les informations relatives à des abonnés différents. Et surtout, par définition il ne doit pas y avoir de doublons, c'est-à-dire qu'un même ensemble de valeurs doit être stocké une fois et une seule. On doit être capable d'identifier séparément chaque ensemble d'attributs, et cela nous amèn
e à la notion de clef, c'est-à-dire un ensemble d'attributs qui identifie sans ambiguïté dans notre cas un abonné. Qu'est-ce-qui permet d'identifier un abonné ? Le nom et l'adresse forment une clef. Mais le numéro de téléphone me permet aussi d'identifier mon abonné. Et le relevé d'identité bancaire aussi. Je peux donc avoir plusieurs clefs possibles.

Mais on arrive ici à un problème informatique classique. Si je veux retrouver les informations que je connais sur l'abonné François Pignon, je vais faire des comparaisons entre ce que j'entre et ce qui est stocké dans la base de données. Si c'est différent au niveau minuscules/majuscules, ou si j'ai par exemple un blanc en trop, pour l'ordinateur ce sont des valeurs différentes et je risque de ne pas retrouver mes données. Une première chose à faire, c'est de standardiser la casse, et séparer en prénom et nom pour éliminer les problèmes d'espace.

Si je veux retrouver mes informations facilement, il faut que mes attributs soient des attributs simples, et cela va me demander, dans la conception de la base de données, une phase de normalisation des données. Par exemple je vais stocker l'adresse sous forme voie, ville et code postal – cela me permettra de rechercher plus facilement ville ou code postal pour des statistiques par exemple. Mais savoir à quel niveau de détail descendre n'est pas évident. Par exemple un même boulevard du Général Leclerc peut être appelé avenue ou rue, le « du » peut manquer, comme Leclerc a été nommé Maréchal à titre posthume le grade peut changer, certaines personnes peuvent être mauvaises en orthographe, et d'autres aimer la simplicité. Si vous voulez faire des statistiques par rue, il faudrait encore découper. Si c'est pour imprimer l'adresse sur une enveloppe, la Poste connait son métier et ce ne sera pas si grave. Où je veux en venir, c'est que la notion d'attribut simple est relative et dépend ce ce que vous voulez faire
des données.

Je reviens à mes abonnés. Je peux donc les identifier soit par leur adresse, soit par leur numéro – je laisse le RIB à la banque. Mais le problème c'est que dans une relation client j'aime bien que mes identifiants ne changent pas. On peut changer de numéro de téléphone. On peut se marier et changer de nom ; on peut déménager. On peut même tout faire en même temps. Pour l'opérateur, la seule manière de s'y retrouver est d'affecter un code client qui lui ne bougera pas, qui sera lui aussi une clef et que l'on appellera la clef primaire.

Mais je n'ai pas fini avec mes soucis. Monsieur et madame ont probablement chacun leur téléphone, et les prélèvements vont être sur un compte commun. Je vais donc me retrouver avec pas mal d'informations communes. Ils peuvent déménager, et si l'on met à jour l'adresse pour un et pas pour l'autre cela risque d'être le souk.

Ce que l'on va faire c'est que l'on va fragmenter les informations en plusieurs relations, chacune avec sa clef, les abonnés, leurs différents contrats avec le numéro de téléphone associé, et pour chaque numéro la consommation mensuelle par exemple. Je suis en train d'abréger sauvagement mais tout ceci s'appelle la modélisation, c'est ultra important et c'est difficile à bien faire.

Le but du jeu c'est d'arriver à ce que dans chaque relation, les attributs qualifient la clef, toute la clef, et rien que la clef. C'est la définition de William Kent, un autre spécialiste des bases de données, et si vous parvenez à cela vous aurez une base de données solide.

Un résultat important de la normalisation c'est l'intégrité des données – le fait que les données soient bonnes et le restent. Avant le relationnel, les programmes devaient effectuer beaucoup de contrôles avant de stocker ou de modifier les données. En mettant les contrôles dans la base, même si un autre programme écrit moins soigneusement accède aux mêmes données, il ne les pourrira pas.

Comment ces contrôles sont-ils effectués ? D'un côté en associant un domaine à chaque donnée, plus ou moins un type, et d'autre part en définissant des contraintes. Les contraintes ont été le cheval de bataille de Chris Date, un autre anglais d'IBM qui a rejoint Codd en Californie et qui est devenu son plus proche collaborateur et ami. Il y a plusieurs types de contraintes, comme par exemple le fait de dire qu'une combinaison de valeurs ne doit se retrouver qu'une seule fois dans une relation, et, ce qui est peut-être le plus intéressant, l'intégrité référentielle, qui revient à dire par exemple que l'on n'accepte un code client dans la relation des contrats que s'il correspond à un abonné existant, un numéro de téléphone dans les consommations que si le numéro existe, et un code postal que si on le trouve dans une relation qui contient tous les codes postaux valides. Cela permet de détecter beaucoup de fautes de frappe !

Mais revenons à Codd. Sa grande idée, c'est que l'on pouvait OPERER sur des relations, au sens d'exécuter des opérations résultant en de nouvelles relations. Il a défini un certain nombre d'opérations, voici les trois principales :

La possibilité de définir par application de critères un sous-ensemble d'une relation, opération qu'il a appelée sélection,

Ne retenir que certains des attributs de la relation originelle, ce qu'il a appelé projection

Et la possibilité de construire une nouvelle relation hybride à partir d'attributs provenant de plusieurs relations et rapprochées par des valeurs communes ; par exemple rapprocher le nom de l'abonné de sa date de souscription en rapprochant la relation des abonnés de celle des contrats sur le code client. Il a appelé cette opération la jointure.

Un autre employé d'IBM, Don Chamberlin, qui était un spécialiste des bases de données de l'époque, a raconté qu'en assistant à une des présentations en interne de ses idées par Codd, il le voyait résoudre en une équation des questions qui lui auraient demandé 5 pages de programmation. Et il s'est dit « Waow, c'est puissant ! »

Codd a également insisté pour que les insertions, mises à jour et destructions puissent être des opérations massives, pas du tout les opérations enregistrement par enregistrement qui étaient habituelles à l'époque. Il a aussi défini que lorsqu'une valeur manquerait, on pourrait stocker à la place quelque chose qu'il a appelé NULL. Et là, il a mis le doigt dans quelque chose qui n'est pas simple. En fait, il peut y avoir à la base deux raisons pour laquelle une valeur manque – soit l'attribut est inapplicable, soit on ne le connait pas. Si vous stockez des informations sur des animaux, par exemple, une durée de gestation peut être manquante parce que l'animal est ovipare, ou parce qu'il s'agit d'un petit mammifère récemment découvert au fin fond de la jungle de Bornéo et que les scientifiques n'ont pas encore pu étudier. Sur le tard, Codd a envisagé de différencier les deux cas, mais cela n'a jamais été fait.

Je vous ai dit que Date était le plus proche collaborateur de Codd, ils ont été associés pendant plusieurs années après avoir quitté IBM. Chris pense que les NULLs étaient une erreur et n'ont rien à faire dans le relationnel. Il a écrit beaucoup de livres, pas « SQL sans les NULLS » mais je pense que c'est un titre qu'il aimerait.

Bon, puisqu'on en parle vous allez me dire « et SQL, dans tout ça ? ». J'y viens. Mais d'abord il faut savoir que le relationnel et SQL, ce n'est pas la même chose. Par exemple, le professeur Stonebraker de Berkeley avait dirigé un projet d'après les idées de Codd, le projet Ingres dont Postgres descend aujourd'hui, qui utilisait au départ Quel, un autre langage que beaucoup trouvaient supérieur à SQL.

SQL, qui s'appelait Sequel au départ, a été inventé par Don Chamberlin et Ray Boyce, décédé malheureusement très jeune d'une rupture d'anévrisme, dans le cadre du principal projet d'IBM autour des idées de Codd, System R. Une des raisons du succès de SQL, c'est peut-être qu'un certain Ed Oates en a lu les spécifications dans des publications d'IBM. Il en a parlé à son ami Larry, qui avait lu le papier de Codd et en avait saisit l'importance, et avec un autre ami et programmeur de première force, Bob Miner, ils ont fondé une société, Software Development Laboratories, et en travaillant comme des fous avec un quatrième mousquetaire, Bruce Scott, ils sont parvenus à sortir avant IBM une base de données qui s'appuyait sur SQL et tournait sur des machines concurrentes de celles d'IBM. Ils ont appelé leur produit Oracle.

Je vais essayer de vous montrer comment Chamberlin et Boyce ont essayé de concrétiser les idées de Codd, non pas avec le SQL de l'époque, mais avec celui d'aujourd'hui, qui a un peu évolué.

Plutôt que de parler de relations ils ont préféré parler de tables, et pour définir une relation ils ont utilisé CREATE TABLE, avec la liste des attributs et leurs types. Dans beaucoup d'outils il faut terminer une commande SQL par un point-virgule. Les types sont très restreints par rapport à ce que Codd envisageait – essentiellement du texte, du numérique, des dates et du binaire. Si vous définissez une table comme cela, c'est du bon SQL mais du mauvais relationnel parce ce que rien n'interdit les doublons. On va donc dire que le code client est la clef primaire, que le code postal n'est valide que si on le trouve dans une table de référence, et que l'on ne peut pas avoir deux abonnés avec le même nom et la même adresse – c'est important, parce que les numéros de clients sont généralement produits automatiquement et protègent contre les doublons « techniques » mais pas les doublons réels. Avec cela Chris Date sera heureux.

A côté de CREATE Chamberlin et Boyce ont défini DROP et ALTER, pour respectivement détruire et modifier une table.

Pour la sélection, SELECT suivi de la liste des noms des colonnes de la table séparés par des virgules, dans n'importe quel ordre, suivi de FROM et du nom de la table. Cela ramène toutes les informations d'une table. On ne le fait jamais dans un programme mais on peut utiliser SELECT * pour ramener toutes les colonnes dans l'ordre du CREATE TABLE.

Et pour filtrer, WHERE suivi d'un nom de colonne et d'une comparaison.

Ce qui est intéressant, c'est que le résultat est une relation au sens de Codd. On peut mettre l'expression entre parenthèses, lui donner un nom, et l'employer comme si c'était une table. On ne fera pas cela pour quelque chose d'aussi simple, on ajoutera simplement d'autres conditions avec AND ou OR. SQL connait les opérateurs de comparaison classiques, il en a qui lui sont propres comme LIKE qui compare du texte à du texte avec un caractère joker (le pourcent), et aussi IS NULL pour vérifier si une colonne est renseignée ou pas.

Pour la projection, c'est encore SELECT, et l'on limite les colonnes à quelques unes ; bien entendu on peut combiner avec une sélection, qui peut s'appliquer à des colonnes que l'on ne ramène pas. Sauf que là, gros problème par rapport aux idées de Codd. Si parmi les colonnes il y a au moins une clef complète, pas de souci. Mais sinon le résultat n'est pas une relation : on a des doublons. Et derrière tout tombe par terre, parce que vouloir appliquer des opérateurs relationnels à des choses qui ne sont pas des relations, c'est comme vouloir calculer des modulos avec des nombres qui ne sont pas des entiers. SQL ne vous dira rien, mais il vous retournera des résultats incorrects. Pour s'en tirer, il faut rajouter DISTINCT, et vous obtiendrez un résultat sans doublon qui est une vraie relation et rendra le sourire à Codd.

Il y a une autre manière de s'en tirer, c'est de rajouter une fonction de groupe, par exemple de compter en précisant par GROUP BY par quoi on groupe. Plus de doublons, et l'on sait que l'on a deux MARTIN. Il y a quelques fonctions de groupe, les plus utilisés sont COUNT(), MAX() et MIN(), et SUM() pour les colonnes numériques. La signification est évidente.

Pour la jointure on ajoute d'autres tables avec JOIN en précisant avec ON sur quelles colonnes s'effectue le rapprochement. Comme souvent des colonnes portent le même nom dans plusieurs tables, on donne un petit nom aux tables, ici a et c, et l'on préfixe chaque nom de colonne par a. ou c. pour préciser dans quelle table se trouve chaque colonne (ce n'est obligatoire que s'il y a ambiguïté, mais c'est aussi plus clair dans les programmes). Les conditions peuvent s'appliquer aux colonnes de n'importe quelle table.

Chamberlin et Boyce se sont aussi sentis obligés d'ajouter quelque chose dont Codd n'avait rien à cirer – l'ordre. La plupart des produits ajoutent aussi une clause LIMIT pour retourner par exemple les cinq abonnés les plus récents. Dans SQL Server il n'y a pas LIMIT mais TOP qui permet de faire la même chose, et dans Oracle on utilise un numéro interne appelé rownum.

SELECT est le gros morceau. Pour ajouter des lignes c'est INSERT INTO suivi du nom de la table et de la liste des colonnes, puis soit d'un SELECT soit d'une clause VALUES.

Pour mettre à jour c'est UPDATE nom de la table SET nom de colonne = valeur, avec une clause WHERE similaire à ce qu'on écrit dans un SELECT.

Pour détruire, DELETE FROM, là encore avec une clause WHERE.

Voilà. C'était rapide, mais c'est une introduction.


Vidéo Gestion d'une collection de films en Python et SQL

Gestion d'une collection de films en Python et SQL

Transcript Vidéo

Passons au concret si vous le voulez bien ! Nous allons développer une petite application en Python qui se connecte à une base de données pour stocker une liste de films que l'on possède.

Vidéo

Gestion d'une collection de films en Python et SQL Transcript

http://www.dailymotion.com/swf/video/xgb3x3_tech


Gestion d'une collection de films en Python et SQL Transcript

Transcript

Vidéo Chargement de données en Python

Bonjour,

Maintenant que vous avez une petite idée de ce qu'est une base de données je vais vous montrer comment réaliser une petite application de type base de données en Python. Je vais d'abord vous expliquer ce que je veux faire, et comment. Ma base de données sera très très simple, mon but n'est pas de vous transformer en experts du domaine mais simplement vous montrer comment un programme peut s'interfacer avec une base de données. Dans cette Pythonnerie, je vous présenterai les données et le contexte. Dans la prochaine Pythonnerie, je vous montrerai comment charger une petite base avec un programme Python. Par la suite, je vous montrerai comment écrire avec wxPython une petite application permettant d'interroger les données et de les mettre à jour.

Ce que je vais gérer, c'est une collection de DVD. Il y aura essentiellement une table, celle des films, avec titre, genre, nom du réalisateur, pays, année de sortie et durée du film en minutes. Qu'est-ce qui identifiera de manière unique un film ? Quelle sera ma clef ? Le titre bien sûr, auquel il faut que je joigne le réalisateur à cause des remakes, et aussi l'année. Si vous ne comprenez pas pour quoi j'ajoute l'année penchez-vous sur la filmographie de Hitchcock, en particulier les années 1934 et 1956 …

Je bornerai mes interrogations à deux choses, isolément ou en combinaison : le titre et le genre. Je ne ferai pas de recherche sur le réalisateur, ce qui me permet de ne pas être trop regardant sur la manière dont j'en stocke le nom. Je ne gère pas non plus les noms des acteurs, ce que je ferais évidemment dans une véritable application mais qui compliquerait assez sérieusement les choses, beaucoup trop pour une présentation rapide.

Rechercher sur le genre est facile. Mais rechercher sur le titre, pas si simple. Pour « Avatar » c'est facile. Mais si vous cherchez « Les Ch'tis » quand le titre officiel et complet du film est « Bienvenue chez les Ch'tis », vous risquez de ne pas trouver le film. Et plus le titre du film est long et compliqué, plus il y a de risque que quelqu'un effectuant une recherche ne se rappelle pas le titre exact ou fasse une faute de frappe.

Il y a un mécanisme qui existe avec un certain nombre de bases de données, qui s'appelle la recherche plein-texte. Cela n'existe pas dans sqlite, le produit que nous allons utiliser, mais il est facile de faire quelque chose d'équivalent.

Quand je vais stocker dans ma base un film, par exemple « La Chevauchée Fantastique », je vais extraire les mots-clefs du film et les stocker sous une forme très normalisée – pas d'accent, et tout en majuscules ; et je vais associer chacun des mots-clefs au film.

Pour extraire les mots-clefs d'un titre, je vais écrire la fonction suivante, mots-clefs.

Tout d'abord, je définis un certain nombre de mots qui sont trop fréquents pour être vraiment significatifs – essentiellement des articles, plus ET. Je vais tout stocker en unicode, ce qui explique que je précéderai en général le texte d'un petit u pour signifier que je peux avoir des caractères autres que de l'ASCII standard. Cette liste est facultative, et vous pouvez y rajouter des mots si vous voulez.

Prochaine étape, j'utilise la fonction enleve_accents() que nous avons vu dans la Pythonnerie 19, et je passe tout en majuscules. Je convertis aussi en « str » (par opposition à unicode) pour la suite.

Je me prépare une liste vide que je remplirai avec les différents mots-clefs. Derrière j'utilise la méthode translate pour remplacer les caractères de ponctuation par des espaces. Je ne suis pas certain d'être exhaustif, mais je ne dois pas en être loin. A ce stade, mon titre est donc passé en majuscules, les accents et la ponctuation ont disparu, et il n'y a plus que des lettres et éventuellement des chiffres.

Je vais donc tout séparer sur les espaces et boucler sur chacun des mots. Là, j'ai un bloc « try » et je demande la position de mon mot - c'est ce que retourne la méthode index() - dans la liste des mots à ignorer. Si je le trouve je ne fais rien. En revanche, s'il n'est pas dans les mots à ignorer je vais avoir une exception et là le mot m'intéresse. Si ce n'est pas un caractère tout seul (là encore, c'est un choix de ma part, vous pouvez considérer ou non que certains mots d'une lettre sont significatifs) je l'ajoute au résultat.

Enfin, je retourne le résultat. Mais je ne vais pas le retourner comme cela. Rappelez vous que les bases de données relationnelles n'aiment pas les doublons. Il y a quelques cas de titres où un même mot apparaît plusieurs fois. Je vais donc transformer ma liste en « set », qui est un objet Python comparable à une liste sauf qu'il ne contient pas de doublon – tout à fait comme les relations de Codd. Cela éliminera les doublons, et derrière je re-transforme en liste pour renvoyer une liste normale.

Je ne vais pas associer chaque mot clef à une combinaison titre, réalisateur, année – cela serait un peu lourd. Je vais donc ajouter une clef primaire artificielle, un numéro que je vais appeler id (comme identifiant) film. Et j'aurai à côté la table mots_clefs avec seulement deux colonnes (dont la combinaison constitue la clef), l'identifiant du film et le mot-clef. Ce n'est pas trop compliqué.

On verra les commandes SQL réelles dans une Pythonnerie ultérieure mais je peux déjà vous expliquer le principe de la recherche. Quand l'utilisateur tapera le titre d'un film, par exemple « la chevauchée fantastique » on pourra utiliser la fonction précédente pour transformer le titre en liste de mots-clefs. A partir de là, je vais rechercher les titres associés à chacun des mots-clefs. Je vais trouver CHEVAUCHEE associé à « La chevauchée de la vengeance », mais aussi à « La chevauchée sauvage », à « chevauchée avec le diable » - et à « la chevauchée fantastique ». Pour FANTASTIQUE, je vais encore trouver « la chevauchée fantastique » et aussi « la charge fantastique ». J'espère que vous vous rappelez le COUNT(*) et le GROUP BY de SQL ; c'est ce que je vais utiliser pour voir combien de fois je trouve chaque film. Le gagnant est bien entendu celui que je trouve le plus de fois.

Maintenant si quelqu'un se rappelle une vague recommandation orale ou est franchement approximatif en orthographe et tape chevaucher E-R je ne vais trouver, si je n'ai aucun titre qui contient chevaucher E-R, que les deux titres qui contiennent « Fantastique ». Dans ce cas, ne pouvant décider, je ramènerai les deux. Si l'utilisateur fait une faute de frappe en tapant « fantastique », là je ne peux rien pour lui.

Je vous ai dit qu'un certain nombre de produits implémentaient une indexation plein-texte similaire, mais gérer les choses soi-même a des avantages. Reprenons par exemple « Bienvenue chez les Ch'tis ». Avec mon algorithme, au moment du stockage je passe tout en majuscules, je remplace l'apostrophe par un blanc, et je vais associer au film les « mots » de deux lettres ou plus qui ne sont pas à ignorer. Si quelqu'un cherche sur « Ch'tis » avec une apostophe, pas de souci vu que c'est le même algorithme que j'appliquerai pour la recherche. Mais si quelqu'un cherche sur « Chtis » en un mot et sans apostrophe, je ne vais pas trouver le mot. Pour éviter cela, je peux facilement ajouter à ma table des mots-clefs quelques variantes, comme le mot CHTIS sans apostrophe, qui pointeront vers le même film.

Les principes étant expliqués, comment vais-je faire avec Python ? Je pourrais utiliser une base de données connue, mais comme je ne veux pas vous en faire installer une, ce qui nous entraînerait un peu loin, je ne vais pas utiliser un « vrai » SGBD, c'est-à-dire un système indépendant, mais sqlite, un produit livré avec Python (et d'autres langages de programmation d'ailleurs). SQLite a été créé par Richard Hipp qui l'a mis dans le domaine public (autrement dit c'est gratuit, et on a le droit de vendre les applications que l'on fait avec sans payer de redevance).. Avec SQLite, le programme ouvre un fichier mais accède aux données contenues dans ce fichier en émettant des ordres SQL. Pour un programmeur, c'est exactement comme s'il travaillait avec SQL Server, Oracle ou MySQL, quelques uns des SGBD les plus connus. Evidemment vous ne ferez pas avec la même chose qu'avec les « grands » produits. Mais pour apprendre SQL et les accès aux bases de données, c'est vraiment très bien, et c'est très utilisé – il n'es
t pas impossible que votre téléphone ou votre lecteur MP3 contienne un ou des fichiers sqlite.

SQLite n'est pas très rigoureux sur le type des données qu'il stocke – vous pouvez dire qu'une colonne contient des entiers et y mettre du texte, il ne se plaint pas. Il y a aussi un ou deux points où il ne se comporte pas exactement comme les autres produits. Pour le prix je ne vais pas me plaindre.

Dans la prochaine Pythonnerie nous écrirons un programme de chargement des données. Par la suite je vous montrerai un programme graphique pour interroger et mettre à jour les données, mais il arrive fréquemment que le chargement initial de la base, au moins celui des tables de référence, soit fait par lecture de fichiers. Très souvent par exemple les gens commencent par gérér les choses dans un tableur, jusqu'au moment où ils se disent qu'ils devraient utiliser quelque chose de plus sérieux. Il faut donc commencer par récupérer les données existantes, et c'est ce que nous ferons.


Vidéo Chargement de données en Python

Chargement de données en Python

Transcript Vidéo

Allons plus loin : nous voulons charger la base de données directement dans un programme en langage Python. Comment procéder ? Nous allons le découvrir !

Vidéo

Chargement de données en Python Transcript

http://www.dailymotion.com/swf/video/xgb3yf_tech


Chargement de données en Python Transcript

Transcript

Vidéo

Bonjour,

Comme annoncé la dernière fois, je vais vous montrer dans cette Pythonnerie comment charger dans un fichier sqlite qui représentera notre base de données un fichier contenant les données initiales.

Comme je ne gère pas du tout mes DVD, même pas dans un tableur, je suis allé chercher une liste de films sur le site du ciné-club de Caen dont voici l'adresse. Vous pouvez utiliser les mêmes données que moi ou d'autres. J'ai copié les données dans mon browser, puis je les ai collées dans Notepad avant de les sauvegarder. Vous pouvez remarquer qu'une information comme le pays est parfois alignée, et parfois pas – c'est typique de ce qui se passe quand une tabulation est utilisée pour séparer des morceaux d'information différents.

Remarque importante : vous vous rappelez je pense tous les problèmes avec les caractères accentués, quand on récupère des données sur Internet de cette manière le codage est en général ce qu'on appelle utf-8 ; c'est aussi ce que stocke sqlite, et ce que l'on choisit souvent comme stockage des caractères dans une base de données. On va en tenir compte dans Python.

Je commence par importer mon module normalisation, dans lequel j'ai non seulement mis la focntion pour enlever les accents mais également celle qui extrait une liste de mots-clefs.

Pour utiliser sqlite, il faut que j'ajoute « import sqlite3 » (comme avec Python, il y a eu une assez grosse différence, pas tant au niveau de la syntaxe SQL qu'au niveau du stockage des données, entre les versions 2 et 3 de sqlite). Il y a des modules pour accéder à toutes les grandes bases de données.

La première chose quand on travaille avec une base de données c'est d'obtenir une connexion à cette base de données. C'est là que se trouve la plus grande différence entre sqlite et un vrai SGBD : pour un vrai SGBD, il faut dire sur quelle machine il se trouve (qui n'est pas forcément celle sur laquelle on est), donner un numéro qu'on appelle le numéro de port qui est nécessaire pour se connecter au SGBD, préciser en général à quelle base on se connecte parce qu'un serveur de bases de données peut en héberger plusieurs, et également fournir un nom de compte et un mot de passe parce que l'on ne se connecte pas « comme ça » à une base de données. Avec SQLite c'est beaucoup plus simple, l'unique paramètre est un nom de fichier qui est là où sqlite rangera les données. S'il n'existe pas déjà, il sera créé. Vous pouvez donnez l'extension que vous voulez, j'ai pris bdd comme base de données, on voit souvent dbf comme « database file », parfois on voit « .sqlite ». N'utilisez pas .sql parce que c'est ce que l'on emp
loie pour des fichiers qui contiennent des commandes SQL, et pas des données.

La méthode cursor() de la connexion vous retourne ce qu'on appelle un curseur, qui est en gros un objet associé à l'exécution d'une commande SQL : pour exécuter une commande SQL, quelle qu'elle soit, la première chose dont on a besoin c'est un curseur.

La première chose que je vais faire dépend de la manière dont je vais utiliser mon programme. Il est fréquent avec un programme de chargement que l'on doive s'y reprendre à plusieurs fois, en général à cause d'erreurs bêtes dans les données, par exemple une donnée normalement obligatoire qui manque, une faute de frappe qui fait qu'une des contraintes définies dans la base ne peut pas être satisfaite, des choses comme cela. En général, on corrige à la main ce qui ne va pas, et on relance. Je vais donc commencer par un bloc try, dans lequel je détruis, avec la commande SQL drop, les tables si elles existent déjà ; cela m'évitera de gérer les erreurs si j'essaie de créer une table qui existe déjà. Si les tables n'existent pas encore, au premier lancement du programme, j'aurai une exception sqlite3.OperationalError, que j'ignorerai en mettant simplement la commande Python « pass ».

Pour créer une table, je passe simplement le texte de la commande SQL à la méthode execute() du curseur. Plusieurs choses à remarquer. Je commence (et termine) le texte de la commande par trois guillemets. C'est une notation Python que je n'ai pas utilisée encore, cela me permet de passer à la ligne dans le texte, ce qui avec SQL est bien pratique parce que cela rend les commandes beaucoup plus lisibles. Autre chose importante, j'ajoute autoincrement à côté de la définition de mon identifiant de film, ce qui fait que sqlite numérotera automatiquement. MySQL utilise une convention similaire, avec d'autres produits c'est parfois différent. Une bizarrerie de sqlite, si l'on met autoincrement il faut que le type soit integer en toutes lettres, « int » ne suffira pas. Vous remarquerez que certaines de mes données sont obligatoires, ce que j'indique par not null, et si je ne mets rien elles sont facultatives par défaut. En fait, j'ai mis obligatoires les données dont je dispose maintenant. Le reste, je le mettrai à jour plus tard.

La table des mots-clefs, c'est identique, sauf que là l'identifiant du film viendra de l'autre table, donc je ne mets pas « autoincrement » mais une contrainte de référence (le vrai terme est « intégrité référentielle ») et je précise que la clef primaire est la combinaison des deux colonnes – je ne mettrai un même mot-clef qu'une fois par film.

Maintenant que j'ai mes tables, je peux lire mon fichier pour les charger. Je vous ai dit que ce que j'avais récupéré sur Internet était de l'utf-8, que je peux facilement transformer en unicode, que sait bien gérer Python, grâce au module codecs. Ce module contient des fonctions d'ouverture de fichier qui ressemblent aux fonctions normales et s'utilisent comme elles, sinon que je précise que mon fichier contient de l'utf-8. Quand je lirai le fichier, la conversion sera automatique.

Ceci demande bien entendu un « import codecs » au début de mon programme.

Maintenant je voudrais introduire une notion très importante. Nous allons voir tout de suite comment ajouter des lignes à une table, disons que j'exécute une commande SQL qui ajoute comme film « Le père noël est une ordure ».. Derrière, j'ajoute dans la table des mots-clefs ceux qui correspondent au titre, d'abord PERE, puis NOEL, et puis là boum problème. Je vous ai dit que souvent un SGBD tournait sur une autre machine. Il peut y avoir un problème réseau, une coupure temporaire. Le serveur peut avoir un problème matériel. Et là je risque de me retrouver avec quelque chose d'à moitié fini. Ce que je veux, c'est que si j'ai « Le père Noël est une ordure » dans la table des films, je me retrouve avec PERE, NOEL, EST et ORDURE dans la table des mots-clefs – tous les mots, pas simplement PERE et NOEL. Ou à la limite, je préfère qu'il n'y ait rien du tout. C'est un peu comme quand vous voulez aller voir un film avec des copains, vous êtes cinq et il ne reste plus que trois places dans la salle – vous préférez dire « – eh bien on ira ensemble à une autre séance ». Là c'est pareil, je pourrai toujours recharger correctement quand le problème sera réglé. C'est une question d'avoir des données cohérentes, la fameuse intégrité des données que les bases de données essaient de garantir.

Cette notion de dire « je veux que tout passe, ou à la limite rien du tout », cela s'appelle la notion de transaction, c'est-à-dire un ensemble de commandes SQL qui doivent toutes passer, ou alors aucune. Avec certains produits, dont sqlite, on commence par exécuter la commande SQL « begin (qui veut dire commence) transaction ». Avec d'autres produits, c'est implicite.

Dans tous les cas, on finit la transaction en appellant soit la méthode commit() de la connexion, pour dire que c'est bon, soit sa méthode rollback(), qui dit « annule tout ce que j'ai fait depuis le début de la transaction ». Vous remarquerez que ce sont des méthodes de la connexion. Vous pouvez avoir plusieurs curseurs en même temps, les opérations transactionnelles sont globales.

Si vous ne dites pas « begin transaction » avec sqlite, chaque commande SQL qui modifie le base est une petite transaction que vous ne pouvez pas annuler.

Vous vous doutez bien que mon programme va consister à boucler sur le fichier en entrée, charger un film, puis charger ses mots clefs, et recommencer avec le film suivant. Au minimum, il faut que je commence une transaction avant de charger le film, puis que je la termine après le chargement de ses mots-clefs. Ce n'est pas ce que je vais faire. En fait, je vais mettre TOUT le chargement dans une transaction – ou je charge tout, ou je ne charge rien.

J'ai deux bonnes raisons pour faire cela : d'abord, quand j'exécute un commit(), le SGBD doit écrire des choses sur un fichier, ce qui ralentit TRES sérieusement les opérations. Vous pourrez essayer, la différence est très marquée. La deuxième raison, c'est que souvent il est plus facile, surtout quand c'est rapide, de tout annuler et de recommencer de zéro plutôt que d'essayer de reprendre une opération en cours. Il ne faut pas avoir peur, j'ai vu des chargements de plusieurs millions de lignes d'un coup (pas dans sqlite !).

Bon, maintenant on peut y aller. J'ai ouvert mon fichier, je commence une transaction.

Je boucle sur les lignes du fichier. Je récupère dans une liste que j'appelle details les différents éléments de chaque ligne. J'élimine de chaque ligne le passage à la ligne final et les espaces aux extrêmités avec la méthode strip(), puis je sépare avec la méthode split() sur le caractère tabulation, qui se représente par une barre de fraction inversée suivie d'un petit t. Comme il n'est pas impossible que les tabulations soient précédées ou suivies d'espaces, j'ai écrit une toute petite fonction nettoie_liste() qui applique la méthode strip() à chacun des éléments de la liste avant de la retourner.

Qu'est-ce que j'ai dans le fichier que j'ai récupéré sur le web ? Le titre, le nom du réalisateur, le pays et l'année dans cet ordre. J'attends donc quatre informations. Première chose, je vérifie que j'ai bien quatre informations – il pourrait y avoir par erreur une tabulation de trop, ou en manquer une, et je risquerais de charger des données fausses. Si je n'en ai pas quatre informations, j'annulle tout avec un rollback, puis je lance une exception qui dit où j'ai rencontré un problème. ValueError n'aime pas trop les accents, j'en tiens compte.

Sinon, je peux charger les informations concernant le film que je viens de lire. Je vous l'avais dit dans ma présentation rapide des bases de données et de SQL, pour ajouter une seule ligne à une table on exécute « insert into « - le nom de la table, ici films – la liste des colonnes etre parenthèses, « values », et entre parenthèses un point d'interrogation par valeur que je fournis. En paramètre additionnel, la liste qui contient les valeurs qui prendront la place des points d'interrogation.

Vous remarquerez que l'identifiant du film n'apparaît nulle part – si vous vous rappelez, j'avais dit à la création de la table qu'il était « autoincrement », c'est-à-dire que sqlite gérait tout seul la numérotation. Sauf que je vais en avoir besoin pour associer à chaque mot-clef l'identifiant du film correspondant ! Je vais en fait retrouver le tout dernier numéro produit par le système dans l'attribut lastrowid du curseur. C'est très classique avec les bases de données – il y a toujours un attribut ou une fonction pour vous donner le numéro le plus récemment créé. A partir de là c'est facile : je prends le titre, c'est-dire l'élément 0 de la liste details, j'en extrais la liste des mots-clef, sur laquelle je boucle pour insérer chaque fois la liste constituée de l'identifiant du film, et du mot-clef.

En fin de boucle, commit() – c'est super-important, si vous ne retrouvez rien dans vos tables c'est que vous l'avez oublié, parce que dans ce cas tout est annulé – je ferme mon curseur, je ferme ma connexion et j'ai fini.

Nous verrons dans la prochaine Pythonnerie comment écrire un programme pour interroger et modifier les données que nous avons chargées dans sqlite. En attendant, si vous utilisez Firefox je peux vous recommander le module d'extension qui s'appelle SQLite Manager – il vous permettra d'ouvrir facilement le fichier dbv.bdd pour regarder ce qu'il y a dedans, et même de vous faire la main sur SQL si vous le souhaitez.


Vidéo