Année Universitaire 2013–2014. Licence de Sciences Économiques L3 Cours d’Informatique Initiation à la programmation Enseignant : Éric Würbel Table des matières I. Généralités 6 1. Motivations & généralités 1.1. Introduction, motivations . . . . . . . . . . . . . . . . . . . . . . . . 1.2. Processeur, programme et exécution . . . . . . . . . . . . . . . . . . 1.2.1. processeur, mémoire . . . . . . . . . . . . . . . . . . . . . . . 1.2.2. instruction . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.3. séquences d’instructions . . . . . . . . . . . . . . . . . . . . . 1.2.4. ruptures de la séquence . . . . . . . . . . . . . . . . . . . . . 1.3. Langage de programmation . . . . . . . . . . . . . . . . . . . . . . . 1.3.1. Nécessité . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.2. Principes de fonctionnement des langages de programmation 1.3.3. Un peu de terminologie . . . . . . . . . . . . . . . . . . . . . 1.3.4. Petite digression : logiciels libres . . . . . . . . . . . . . . . . 1.4. Algorithmes et programmation . . . . . . . . . . . . . . . . . . . . . 1.4.1. Algorithmes . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.2. Bonnes habitudes . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.3. De l’analyse du problème à l’exécution du programme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . II. Python 7 7 7 7 8 8 11 14 14 15 15 15 16 16 16 17 18 2. Introduction à Python 19 2.1. Introduction, historique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 2.2. Exemple introductif et méthode de décomposition . . . . . . . . . . . . . . . . . . . . 19 3. Les 3.1. 3.2. 3.3. bases Conventions typographiques . . . . . . . . . Premiers pas, l’interpréteur . . . . . . . . . Python est une calculatrice . . . . . . . . . 3.3.1. Nombres . . . . . . . . . . . . . . . . 3.3.2. Chaı̂nes de caractères . . . . . . . . 3.3.3. Listes . . . . . . . . . . . . . . . . . 3.3.4. Premiers pas vers la programmation. 4. Entrées et sorties 4.1. La sortie . . . . . . . . . . . . . . . . 4.1.1. Utiliser un module . . . . . . 4.1.2. Affichage simple . . . . . . . 4.2. L’entrée . . . . . . . . . . . . . . . . 4.2.1. Entrée d’entiers . . . . . . . . 4.2.2. Entrée de chaı̂ne de caractère . . . . . . . . . . . . . . . . . . 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 22 22 22 22 24 25 26 . . . . . . 28 28 28 28 29 29 29 Table des matières 5. Les structures de contrôle 5.1. L’instruction while et la notion de condition 5.1.1. Conditions simples . . . . . . . . . . 5.1.2. Conditions complexes . . . . . . . . 5.2. L’instruction if . . . . . . . . . . . . . . . . 5.2.1. Version simple . . . . . . . . . . . . 5.2.2. Version avec alternative . . . . . . . 5.2.3. version avec plusieurs alternatives . 5.3. La boucle for . . . . . . . . . . . . . . . . . 5.3.1. La fonction range() . . . . . . . . . . 6. Les 6.1. 6.2. 6.3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 32 32 36 37 37 38 39 39 40 fonctions 41 Déclaration et utilisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 Valeurs par défaut des paramètres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 Les paramètres nommés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 3 Table des figures 1.1. Organisation simplifiée de la mémoire. . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 1.2. Exécution d’un programme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 1.3. Déroulement du programme de la table 1.4 . . . . . . . . . . . . . . . . . . . . . . . . 13 4.1. Saisie et affichage d’un entier. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 4.2. Saisie et affichage d’un texte. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 4 Liste des tableaux 1.1. 1.2. 1.3. 1.4. Jeu d’instruction du processeur exemple. . . . . . . . . . . . . . . . Exemple de programme : calcul du prix TTC à partir du prix HT. Instructions de branchement conditionnel et de test. . . . . . . . . Programme de calcul de la TVA avec application de frais de port. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 . 9 . 12 . 14 5.1. 5.2. 5.3. 5.4. Les opérateurs de comparaison arithmétiques. Les opérateurs de comparaison sur les chaı̂nes Opérateurs de comparaison sur les listes. . . . Tables de vérité des opérateurs logiques. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 . . . . . . . . de caractères. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 34 35 37 Première partie . Généralités 6 1. Motivations & généralités 1.1. Introduction, motivations Une initiation à la programmation pour des non-informaticiens, pour quoi faire ? Plusieurs raisons soutendent ce choix. En premier lieu, apprendre à écrire des petits programmes permet de mieux comprendre le fonctionnement d’un ordinateur. Même pour un non-informaticien, cela est utile, car cela permet de mieux mesurer les capacités d’un ordinateur — ce qu’il est à même de faire ou de ne pas faire. Dans le même esprit, cela permet de démystifier l’ordinateur, de ne pas le voir comme une boite magique , et donc encore une fois de mieux apréhender ses possibilités. Apprendre a programmer c’est aussi être à même d’écrire des petits programmes répondant à des besoins professionnels ou personnels ponctuels. Enfin, la programmation est une formation de l’esprit. Convevoir un programme nécessite de maı̂triser une méthodologie qui peut être utile dans d’autres domaines : savoir décomposer un problème en problèmes plus petits et plus faciles à résoudre, formaliser logiquement l’énoncé d’un problème ou d’une question sont autant de capacités utiles même en dehors de la programmation. Dans ce cours, nous aborderons la programmation par l’apprentissage d’un langage nommé Python. Ce langage est structuré, modulaire, à objet, et offre un cadre sufisamment rigoureux à l’apprentissage de la programmation. De plus, sa bibliothèque d’objets et de fonctions riche permet de rapidement traiter des problèmes intéressants. À partir des bases apprises dans ce cours, il est facile d’apprendre un nouveau langage par la suite. Bien entendu, ce cours n’étant pas destiné à des informaticiens, nous ferons un certain nombre d’hypothèses simplificatrices, dans le but de donner l’idée sans rentrer dans les complications techniques. Ce cours s’articule de la façon suivante : dans une première partie, nous ferons quelques rappels sur l’architecture d’un ordinateur, et plus particulièrement sur le rôle du processeur et de la mémoire. De là nous dégagerons la nécessité d’un langage de programmation qui soit aisément compréhensible par l’être humain et qui permette d’exprimer facilement des algorithmes (nous définirons ce terme plus loin) en vue de résoudre des problèmes. La deuxième grande partie concerne l’apprentissage du langage en lui même. Étant donné le volume horaire réduit des cours (10h), une grande partie de l’apprentissage en lui même se fera en TD. 1.2. Processeur, programme et exécution 1.2.1. processeur, mémoire Le cœur d’un ordinateur est constitué du processeur et de la mémoire centrale. Ces deux composants constituent l’unité active d’un ordinateur. Le processeur est là pour effectuer des instructions (opérations) en séquence (les unes après les autres). Les instructions effectuées agissent généralement sur des données et produisent des résultats. C’est à ce stade qu’intervient la mémoire centrale. C’est dans celle ci que le processeur va aller chercher les opérandes sur lesquelles une instruction agit. C’est souvent dans cette même mémoire que le processeur va écrire (stocker) les résultats d’une instruction. 7 1. Motivations & généralités 1 2 Contenu de la mémoire 3 4 Adresses 5 6 7 8 9 Figure 1.1.: Organisation simplifiée de la mémoire. 1.2.2. instruction Les instructions utilisables par un processeurs sont extrèmement simples. Ce sont des opérations arithmétiques (additions, soustractions, etc.) ou logiques (et, ou ...). On dit que le processeur exécute une instruction. L’exécution d’une instruction est constituée de : – la préparation des opérandes de l’instruction (lecture) – la réalisation du calcul – le stockage du résultat (écriture). C’est à peu près tout ce qu’un processeur sait faire. L’intéret de l’ordinateur est que le processeur sait exécuter ces instructions les unes après les autres à une cadence très élevée. 1.2.3. séquences d’instructions Ce qui distingue l’ordinateur de la calculette, c’est que l’ordinateur est capable d’enchaı̂ner les instructions séquentiellement. Dès qu’il a terminé l’exécution d’une instruction, il va chercher l’instruction suivante et l’exécute à son tour. Même quand l’ordinateur ne fait rien , en fait il excécute des instructions qui ne font rien (pour simplifier). Une question se pose alors : ou l’ordinateur vat-il chercher les instructions à exécuter. La réponse est simple : dans la mémoire centrale, la même mémoire dans laquelle il trouve les données à traiter et dans laquelle il stocke les résultats de ses calculs. Sans rentrer dans les détails, nous allons avoir besoin d’avoir une représentation de cette mémoire. Une hypothèse raisonnable est de voir la mémoire comme une file de cases, chacune de ces cases contenant une information élémentaire (un nombre en fait, l’ordinateur ne connait que ça). Pour s’y retrouver, et pourvoir désigner une de ces cases en particulier, on les numérote, en partant de zéro. ces numéros sont appelés adresses. Les adresses permettent de désigner sans équivoque un emplacement mémoire particulier. Une représentation graphique de ce modèle est donnée à la figure 1.1. Comme nous l’avons dit précédemment, la mémoire contient les données à traiter, les résultats produits et les instructions à exécuter. Cela nécessite un minimum d’organisation pour ne pas tout mélanger. C’est le système d’exploitation qui est généralement chargé de cette tâche. En règle générale, chaque programme se voit attribuer : – une portion de mémoire dans laquelle sont contenues les instructions à exécuter. C’est ce qu’on appelle le segment des instructions, ou encore segment de code. 8 1. Motivations & généralités Code 1 Taille 4 Opérandes 3 Description Effectue l’addition des deux nombres situés aux adresses données par les deux premières opérandes, et stocke le résultat à l’adresse donnée par la troisième opérande 2 4 3 Idem précédement, mais effectue une soustraction. 3 4 3 Idem précédement mais effectue une multiplication 4 4 3 Idem précédemment ais effectue une division 5 3 2 Calcule l’opposé du nombre situé à l’adresse donnée par la première opérande, et stocke le résultat à l’adresse donnée par la deconde opérande 6 2 1 Calcule l’opposé du nombre situé à l’adresse donnée par l’opérande, et stocke le résultat à la même adresse. Chaque instruction occupe une case mémoire, et est suivie d’autant de cases que nécessaire pour stocker les opérandes. Table 1.1.: Jeu d’instruction du processeur exemple. adresse 0 1 2 3 4 5 6 7 contenu 3 0 1 5 1 0 5 2 La table représente le contenu du segment de code du programme. Ce programme s’attend à trouver le prix HT à l’adresse 0 du segment de données, et le taux de TVA à l’adresse 1 du segmnt de données (ce segment n’est pas représenté ici). Le résultat est stocké à l’adresse 2. Table 1.2.: Exemple de programme : calcul du prix TTC à partir du prix HT. – une portion de mémoire dans laquelle sont stockées les données à traiter et les résultats produits. C’est ce qu’on appelle le segment de données. Le système, en coopération avec le processeur, est chargé de vérifier que les programmes n’essaient pas d’accéder à la mémoire en dehors des segments qui leurs ont été alloués. Exemple Pour pouvoir illustrer ces propos par quelques exemples, je vous propose de décrire une machine fictive, très simple, équipée d’un processeur fictif, associé à un modèle de mémoire tel que celui présenté figure 1.1. Nous avons vu que la mémoire ne contient que des nombres (encore une fois et au risque de me répéter, l’ordinateur ne comprend que ça). Les instructions destinées au processeur sont donc des nombres. Pour en revenir à notre processeur simplifié, nous supposerons qu’il comprend les instructions décrites dans la table 1.1. Comme vous le voyez, notre processeur ne sait pas faire grand chose, mais il le fait vite. Pour simplifier, nous supposerons que chaque case mémoire peut contenir un nombre de taille quelconque, entier ou non. La table 1.2 représente le segment de code d’un programme calculant un prix TTC à partir d’un prix HT stocké à l’adresse 0 du segment de données du programme. Un exemple d’exécution de ce programme est proposé à la figure 1.2. Cette exécution se déroule comme suit : 1. En haut à gauche de la figure 1.2 se trouve l’état du segment de code et du segment de données 9 1. Motivations & généralités segment de code segment de code segment de données segment de données 0 0 3 0 0.196 1 1 0 0.196 1 2 1 2 2 1 2 3 5 3 3 5 3 4 1 4 4 1 4 5 0 5 5 0 26.264 5 6 5 6 6 5 6 7 2 7 2 0 3 1 134 134 0 Avant l'exécution de la 1ère instruction Après l'exécution de la 1ère instruction segment de code segment de code segment de données segment de données 0 0 3 0 0.196 1 1 0 0.196 1 2 1 2 2 1 160.264 2 3 5 3 3 5 3 4 1 4 4 1 4 5 0 26.264 5 5 0 26.264 5 6 5 6 6 5 6 7 2 7 2 0 3 1 134 Avant l'exécution de la 2ème instruction 134 0 Après l'exécution de la 2ème instruction Figure 1.2.: Exécution d’un programme 10 1. Motivations & généralités avant l’exécution de la première instruction. À l’adresse 0 du segment de données, on trouve le nombre (le montant) auquel on veut appliquer le calcul de la TVA. À l’adresse 1 se trouve le nombre représentant le taux de TVA. Les quatres cellules du segment de code coloriées en vert représentent l’instruction qui va être exécutée. Décodons cette instruction à l’aide de la table 1.1 : a) Le 3 situé à l’adresse 0 indique au processeur qu’il s’agit d’une multiplication. b) Le 0 situé à l’adresse 1 indique au processeur que le premier nombre à multiplier se trouve à l’adresse 0 du segment de données. c) Le 1 situé à l’adresse 2 indique au processeur que le second nombre à multiplier se trouve à l’adresse 1 du segment de données. d) Le 5 situé à l’adresse 3 indique au processeur qu’il devra ranger le résultat de la multiplication à l’adresse 5 du segment de données. En résumé, on se prépare à effectuer la multiplication montant HT × taux T V A. 2. En haut à droite de la figure 1.2 on trouve l’état du segment de code et du segment de données après l’exécution de la première instruction. La différence avec la situation précédente est qu’on trouve à l’adresse 5 du segment de données le résultat de la multiplication décrite précédemment. 3. En bas à gauche de la figure 1.2 se trouve l’état du segment de code et du segment de données avant l’exécution de la seconde instruction. On rapelle qu’à l’adresse 0 du segment de données, on trouve le nombre (le montant) auquel on veut appliquer le calcul de la TVA, à l’adresse 1 se trouve le nombre représentant le taux de TVA et enfin à l’adresse 5 se trouve le résultat le la multiplication du montant par le taux de TVA. Les quatres cellules du segment de code coloriées en vert à partir de l’adresse 4 du segment de code représentent l’instruction qui va être exécutée. Décodons cette instruction à l’aide de la table 1.1 : a) Le 1 situé à l’adresse 4 du segment de code indique qu’il s’agit d’une addition. b) Le 0 situé à l’adresse 5 du segment de code indique que le premier nombre à additionner est situé à l’adresse 0 du segment de données (il s’agit donc du montant HT). c) Le 5 situé à l’adresse 6 du segment de code indique que le second nombre à additionner se situe à l’adresse 5 du segment de données (il s’agit du résultat de l’opération précédente). d) Le 2 situé à l’adresse 7 du segent de code indique que le résultat de l’addition doit être rangé à l’adresse 2 du segment de données. Autrement dit, on additionne le montant HT au résultat calculé à l’étape précédente. 4. En bas à droite de la figure 1.2, on trouve les segments de code et de données après l’exécution de la deuxième instruction. Le résultat du calcul est rangé à l’adresse 2 du segment de données. Au final, cette adresse contient un nombre correspondant au calcul de (montant HT ×taux T V A)+ montant HT 1.2.4. ruptures de la séquence Dans tout ce que nus avons vu précédemment, le processeur va chercher les instructions à exécuter dans le segment de code en commençant à la plus petite adresse de ce segment et en allant vers les adresses plus élevées. Toutefois, ce mode de fonctionnement ne permet pas certains traitements. En partant de l’exemple précédent, imaginez que nous voulions appliquer des frais d’envois de 10¿ au prix TTC si celui-ci est inférieur ou égal à 100¿. Comment faire comprendre au processeur que, sous certaines conditions, il doit effectuer certains calculs 1 ? Cela va nécessiter d’enrichir le jeu d’instructions de notre processeur. On ajoute donc à notre processeur fictif quelques instructions 11 1. Motivations & généralités Code 7 Taille 3 Opérandes 2 8 3 2 9 2 1 Description tester si la première opérande est inférieure à la seconde. Le résultat du test (VRAI ou FAUX), est stocké de façon interne par le processeur. Ce résultat peut être exploité par l’instruction de code 9. tester si la première opérande est supérieure à la seconde. Le résultat du test (VRAI ou FAUX), est stocké de façon interne par le processeur. Ce résultat peut être exploité par l’instruction de code 9. si le dernier test (¡ ou ¿) effectué a donné un résultat faux, alors continuer l’exécution à l’adresse indiquée par l’opérande. Dans le cas contraire continuer l’exécution en séquence. Table 1.3.: Instructions de branchement conditionnel et de test. supplémentaires présentées dans la table 1.3. En particulier, la dernière instruction de cette table permet de faire ce qu’on appelle un branchement conditionnel, c’est à dire qu’elle permet d’introduire une rupture dans la séquence d’exécution du programme : sous une certaine condition, on demande au processeur de continuer l’exécution du programme à une certaine adresse. La table 1.4 présente le programme permettant d’effectuer le calcul décrit précédemment, et la figure 1.3 montre le déroulement de ce programme. Cette figure considère que les deux premières instructions du programme ont déjà été calculées (i.e. le prix TTC a été calculé). Ces deux premières instructions sont identiques au premier programe. Examinons le déroulement de ce nouveau programme : 1. En haut à gauche sont représentés l’état du segment de code et l’état du segment de données juste avant l’exécution de la troisième instruction. Décodons cette instruction : a) le 8 situé à l’adresse 8 du segment de code indique qu’il s’agit d’une comparaison de type supérieur . b) Le 2 situé à l’adresse 9 indique que la première opérande se situe à l’adresse 2 du segment de données. Il s’agit donc du prox TTC calculé précédemment. c) Le 6 situé à l’adresse 10 indique que la seconde opérande se situe à l’adresse 6 du segment de données (i.e. la valeur 100). L’instruction va donc effectuer la comparaison 160.264 > 100. 2. En haut à droite de la figure 1.3 se trouvent l’état du segment de code et l’état du segmnt de données après l’exécution de la troisième instruction. La comparaison effectuée est vraie, aussi le processeur stocke-il la valeur VRAI en interne. 3. En bas à gauche de la figure 1.3 se trouvent l’état du segment de code et l’état du segment de données avant l’exécution de la quatrième instruction. Décodons cette instruction : a) Le 9 situé à l’adresse 11 du segment de code indique qu’il s’agit d’une instruction de débranchement conditionnel. b) Le 17 situé à l’adresse 12 du segment de code indique l’adresse de débranchement. Cela signifie que si le dernier test effectué par le processeur a fourni la réponse VRAI, alors le processeur doit poursuivre l’exécution du programme à l’adresse 17. Ce qui est le cas ici. 1. L’an dernier, avec excel, vous utilisiez la fonction SI. 12 1. Motivations & généralités segment de code segment de données segment de code segment de données 8 8 0 134 8 8 0 134 9 2 1 0.196 9 2 1 0.196 10 6 2 160.264 10 6 2 160.264 11 9 3 11 9 3 12 17 4 12 17 4 13 1 5 26.264 13 1 5 26.264 14 2 6 100 14 2 6 100 15 7 7 10 15 7 7 10 16 2 16 2 17 ... 17 ... avant exécution 3ème instruction segment de code valeur VRAI stockée dans le processeur après exécution 3ème instruction segment de données 8 8 0 134 9 2 1 0.196 10 6 2 160.264 11 9 3 12 17 4 13 1 5 26.264 14 2 6 100 15 7 7 10 16 2 17 ... valeur VRAI stockée dans le processeur exécution 4ème instruction Figure 1.3.: Déroulement du programme de la table 1.4 13 1. Motivations & généralités adresse contenu 0 3 1 0 2 1 3 5 4 1 5 0 6 5 7 2 8 8 9 2 10 6 11 9 12 17 13 1 14 2 15 7 16 2 17 ... Table 1.4.: Programme de calcul de la TVA avec application de frais de port. Exercice Quel serait le déroulement du programme et le contenu du segment de données si l’adresse 6 du segment de données contenait la valeur 200 ? 1.3. Langage de programmation 1.3.1. Nécessité Les programmes présentés dans la section précédente comportent de nombreux inconvénients : – Si on examine le contenu d’un segment de code, il est très difficile de dire ce que fait le programme. Cela nécessite une traduction de la série de nombres contenus dans le segment de code en un énoncé intelligible. – Le contenu du segment de données est très obscur : – On se sait pas à priori ce qu’il contient. – On connait pas non plus le rôle joué par son contenu (donnée, résultat intermédiaire, résultat final ?) – Écrire un programme réalisant une traitement complexe est très ardu Et pourtant notre processeur virtuel est très simple ! Il ne sait exécuter qu’une dizaine d’instructions différentes, et le modèle d’exécution de ces instruction est extrêmement simplifié. Songez qu’en comparaison, des processeurs comme ceux qui sont présents dans les ordinateurs d’aujourd’hui ont un jeu d’instruction comprenant des centaines d’instructions ! De toutes ces constatations est né le besoin de posséder un (des) langage(s) qui permettent : – L’expression simple d’idées complexes. – Une relecture des programmes facile par l’être humain. – La possibilité d’écrire des programmes sans se préoccuper de l’architecture du processeur sousjacent. – une syntaxe rigoureuse, c’est à dire sans ambiguités, claire et suffisamment contraignante pour que les programmes soient lisibles. 14 1. Motivations & généralités On parle de langage de programmation de haut niveau. Bien entendu, in fine, c’est le processeur qui va exécuter les programmes. Il va donc falloir prévoir un mécanisme de traduction permettant de passer d’un programme écrit dans un langage de haut niveau à un programme compréhensible par le processeur (c’est à dire un programme en langage machine). 1.3.2. Principes de fonctionnement des langages de programmation Un langage de programmation de haut niveau est incompréhensible par le processeur. En revanche, son intérêt est d’être compréhensible par l’être humain. Il est donc nécessaire d’avoir un mécanisme de traduction des programmes écrits à l’aide du langage de programmation en programmes en langage machine. Cette traduction est assurée par ... un programme ( !). Il existe à ce titre trois grandes familles de langages de programmation : Les langages compilés Dans ces langages, on traduit une fois pour toutes le programme écrit dans le langage de haut niveau en un programme en langage machine pour le processeur cible. Pour cela, on utilise un programme spécialisé appelé compilateur. Puis on exécute autant de fois qu’on le désire le programme ainsi compilé. Les langages interprétés Dans les langages interprétés, un programme spécialisé, l’interpréteur, traduit à chaque excécution les instructions du programme écrit dans un langage de haut niveau en des instructions en langage machine. Les langages semi-compilés Ici, un programme est traduit par un compilateur dans un langage intermédiaire que sait interpréter un programme annexe appelé la machine virtuelle. Ce langage intermédiaire est proche d’un langage machine, mais sans dépendre d’un matériel particulier. Chaque famille possède ses avantages et ses inconvénients, et l’énoncé et l’analyse de ceux-ci sort du cadre de ce cours. Notons toutefois que les langages compilés sont en règle générale nettement plus rapide que les langages interprétés. Dans le cadre de ce cours, nous utiliserons un langage semicompilé. 1.3.3. Un peu de terminologie Le programme qu’on écrit à l’aide d’un langage de programmation est appelé programme source. Ce programme s’écrit tout simplement avec un éditeur de texte et on le sauvegarde dans un fichier. L’extension attribuée au fichier dépend du langage. Pour Python, l’extension communément admise est .py. Python peut fonctionner en mode interprété ou semi-compilé. La compilation d’un programme source python produit du code intermédiaire (bytecode) pour la machine virtuelle Python. Un fichier contenant du code intermédiaire se termine par l’extension .pyc. 1.3.4. Petite digression : logiciels libres Toutes les applications que vous utilisez couramment (traitement de texte, navigateur internet, jeux, etc...) sont programmés à l’aide de langages de haut niveau. Sur votre CD d’installation, vous n’avez généralement que le programme exécutable, et vous ne pouvez pas savoir comment est écrit le programme (sauf à décoder les instructions en langage machine, tâche quasi-impossible vu la complexité de ces programmes). Le programme source est souvent considéré comme un secret industriel auquel vous n’avez pas accès. Toutefois, il existe une catégorie de logiciels pour lesquels le programme source est fourni avec le support de distribution. C’est ce qu’on appelle les logiciels libres. Le modèle de diffusion de ces logiciels est basé sur les principes suivants : – Vous avez la liberté d’examiner le programme source et de le modifier. 15 1. Motivations & généralités – Vous avez la liberté de redistribuer ce logiciel, pourvu que vous le redistribuiez intégralement, et que vous signialiez les éventuelles modifications que vous y avez apporté sans pour autant modifier la paternité originale du logiciel. Notez que ce modèle de distribution n’implique pas nécessairement la gratuité. Libre ne signifie pas gratuit 2 . Une société peut tout à fait envisager de diffuser un logiciel en utilisant le modèle de diffusion des logiciels libres tout en faisant commerce autour de cette diffusion : vente de manuels imprimés, bénéfice sur la vente de supports contenant ce logiciel, offre de formation, d’assitance, etc... 1.4. Algorithmes et programmation 1.4.1. Algorithmes Le mot informatique est un mot français ( computer science en anglais) désignant la science qui traite de façon automatisée des données pour obtenir des informations (résultats). Ce traitement est traditionnellement effectué à l’aide d’algorithmes. Un algorithme est un mode opératoire à utiliser pour résoudre un problème. Il comporte la liste des objets nécessaires à la résolution et la liste des opérations à réaliser pour résoudre le problème. L’algorithmique est la partie de l’informatique qui traite des algorithmes. Exemples Un algorithme de résolution de l’équation ax + b = 0 est : 1. Soustraire b à gauche et à droite du signe =. On obtient ax = −b ; 2. Si a est non nul diviser chaque membre par a. On obtient le résultat cherché qui est x = − ab ; 3. Si a est nul l’équation est insoluble. Un algorithme de mise en marche d’une voiture est : 1. Mettre la clé dans le démarreur ; 2. Serrer le frein à main si ce n’est pas fait ; 3. Mettre le levier des vitesses au point mort si ce n’est pas fait ; 4. Répéter les opérations suivantes : – tourner la clé dans le sens des aiguilles d’une montre ; – attendre quelques secondes : – mettre la clé dans la position marche ; – si le moteur ne tourne pas, ramener la clé dans sa position initiale ; – jusqu’à ce que le moteur démarre. 5. appuyer sur la pédale d’embrayage ; 6. Enclencher la première vitesse ; 7. Desserrer le frein à main ; 8. Lacher la pédale d’embrayage. 1.4.2. Bonnes habitudes Le but de toute personne désirant programmer est d’écrire des programmes justes, simples, lisibles par d’autres, fiables et efficaces. Pour cela les quelques points suivants sont fondamentaux (liste non exhaustive !) : 1. Réfléchir et imaginer de bons algorithmes de résolution avant d’écrire la première ligne du programme. 2. Contrairement à ce que les credos de la société actuelle tendent à nous faire croire. 16 1. Motivations & généralités 2. Une fois l’algorithme trouvé, en écrire l’esquisse en français, puis le coder dans le langage de programmation choisi. 3. Lors du codage : – choisir des noms parlants pour représenter les objets manipulés dans le programme ; – commenter chaque morceau du programme de manière explicative et non descriptive – tester chaque morceau du programme séparément ; 1.4.3. De l’analyse du problème à l’exécution du programme Voici une marche à suivre pour la création de programmes à partir d’un problème donné. 1. Bien lire l’énoncé du problème, être certain de bien le comprendre. 2. Réfléchir au problème, déterminer les points principaux à traiter. 3. Trouver un bon algorithme de résolution, l’écrire sur papier et en français. 4. Coder l’algorithme en un programme écrit sur papier. 5. Introduire le programme dans l’ordinateur au moyen d’un éditeur. 6. Vérifier la syntaxe du programme au moyen d’un vérificateur de syntaxe. 7. Compiler le programme 8. Exécuter le programme, vérifier son bon fonctionnement par des tests En cas d’erreurs de syntaxe ou d’erreurs à la compilation il faut les corriger avec l’éditeur puis reprendre au point 6. Les points 6. et 7. sont confondus dans la pratique, la vérification syntaxique d’un programme étant faite par le compilateur. Si le programme fonctionne mais donne des résultats faux, cela signifie qu’il y a des erreurs de logique. Il faut réfléchir, les trouver, modifier le programme en conséquence puis reprendre au point 6. 17 Deuxième partie . Python 18 2. Introduction à Python 2.1. Introduction, historique Python est un langage moderne, sa première version date de 1989. Il a été créé au départ pour servir de langage de script pour un projet de système d’exploitation (amoeba). Son nom vient de ce que son concepteur est un grand fan de la troupe comique cinématographique anglaise Monty Python (les connaisseurs en retrouveront des traces dans les exemples utilisés dans ce cours, eux mêmes tirés du tutoriel Python). C’est un langage impératif, c’est à dire que ses instructions s’exécutent en séquence. Il possède toutefois certains mécanismes issus d’autres paradigmes de programmation, comme par exemple la programmation fonctionnelle. C’est un langage à objet. Python peut fonctionner en mode interprété ou en semi-compilé. Il est distribué sous licence libre GNU. 2.2. Exemple introductif et méthode de décomposition Nous allons examiner un problème (simple) dont la résolution sera effectuée en respectant la marche à suivre donnée précédemment (cf. section 1.4.3), restreinte aux points 1 à 4. Pour trouver un algorithme de résolution (points 2 et 3) nous allons appliquer une méthode qui devrait être utilisée chaque fois qu’un problème de programmation doit être résolu. Cette méthode est connue sous le nom de méthode de décomposition par raffinements successifs. Nous allons également nous baser sur cet exemple pour rappeler des bonnes habitudes de programmation et pour introduire les premières notions de programmation en langage Python. L’exemple est le suivant : connaissant une heure en France (heure d’hiver), afficher l’heure universelle correspondante. Concernant le point 1 il faut déjà s’assurer de la clarté de l’énoncé. Dans notre cas, il s’agit d’avoir une définition précise de ce qu’est l’heure universelle . Après renseignement, on apprend que l’heure universelle a un décalage de -1 heure avec l’heure d’hiver en France. Du point de vue de la compréhension, nous avons donc tous les éléments nécessaires. Le point deux nous amène à lister les points à traiter suivants : 1. savoir ou récupérer l’heure à traiter 2. savoir calculer l’heure universelle 3. savoir afficher l’heure universelle (le résultat du calcul précédent) Nous allons maintenant écrire un algorithme, en français, permettant de traiter les points précédents. En première approche, cet algorithme reprend les points à traiter précédemment : 1. récupérer l’heure à traiter 2. calculer l’heure universelle 3. afficher l’heure universelle calculée Nous allons maintenant affiner cet algorithme : 1. récupérer l’heure à traiter 19 2. Introduction à Python a) déclarer un(des) objet(s) pouvant contenir l’heure (heures et minutes) b) initialiser les objets déclarés avec l’heure à traiter 2. calculer l’heure universelle a) soustraire une heure à l’heure à traiter 3. afficher l’heure universelle calculée Un deuxième raffinement pourrait être : 1. récupérer l’heure à traiter a) déclarer un(des) objet(s) pouvant contenir l’heure (heures et minutes) b) initialiser les objets déclarés avec l’heure à traiter 2. calculer l’heure universelle a) soustraire une heure à l’heure à traiter i. si l’heure actuelle est égale à 0, la nouvelle heure est 23 ii. si l’heure actuelle est supérieure à 0, soustraire une heure 3. afficher l’heure universelle calculée Nous en avons terminé avec le point 3. Nous allons passer au point 4., c’est à dire le codage dans le langage choisi (ici Python) : import easygui heures = 22 minutes = 13 if heures == 0: heures = 23 else : heures = heures - 1 easygui . msgbox ( ’ heure universelle : ’ + heures + ’: ’ + minutes ) Ce programme est correct, simple, fonctionne parfaitement mais est horriblement mal écrit. En effet il faut savoir ou se rappeler que – un programme doit être lisible, ce qu’il n’est pas – un programme doit être commenté, ce qu’il n’est pas – un programme doit refléter les décisions prises par le programmeur (ici heure initiale). – les nombres entiers peuvent signifier n’importe quoi ! Il faut préciser leur signification en leur substituant des noms parlants. De plus, aucune interactivité avec l’utilisateur n’a été prévue : l’heure à traiter est interne au programme. Si on veut traiter une nouvelle heure, il faut modifier le pogramme. Voici une deuxième tentative qui tient compte de toutes ces remarques : # -* - coding : utf -8 -* # Calcul de l ’ heure universelle # Auteur : Jean Dupont # Date : 10/01/2014 # modules externes n é cessaires import easygui # quelques constantes 20 2. Introduction à Python derniere_heure = 23 # derni è re heure de la journ é e premiere_heure = 0 # premi è re heure de la journ é e decalage = -1 # d é calage avec le fuseau universel # Saisie de l ’ heure à traiter heures = easygui . integerbox ( " Saisissez les heures " , lowerbound =0 , upperbound =23) minutes = easygui . integerbox ( " Saisissez les minutes " , lowerbound =0 , upperbound =59) # Transformation de l ’ heure nouvelle_heure = heures if nouvelle_heure == premiere_heure : nouvelle_heure = derniere_heure else : nouvelle_heure = nouvelle_heure - 1 # Affichage du r é sultat easygui . textbox ( ’R é sultat ’ , ’ ’ , [ " L ’ heure universelle correspondant à l ’ heure fran caise " , str ( heures ) + ’: ’ + str ( minutes ) + ’ est ’ + str ( nouvelle_heure ) + ’: ’ + str ( minutes ) ]) 21 3. Les bases 3.1. Conventions typographiques Quelques conventions utilisées dans ce document : – Les mots apparaissant en caractères gras sont des mots réservés du langage PASCAL. Ils devront être tapés tels que – Les mots en italique doivent être remplacés par leur définition donnée ailleurs. – Les exemples de programmes, ou les morceaux de programmes cités dans du texte apparaı̂tront en caractères à chasse fixe. – Les exemples interactifs réalisables avec un interpréteur Python ainsi que les programmes sont présentées dans des boı̂tes à fond vert clair. 3.2. Premiers pas, l’interpréteur Python est un langage interprété, c’est à dire que pour exécuter un programme python, vous soumettez le fichier contenant le texte de votre programme à une programme spécial, appelé interpréteur python, qui va traduire à la volée votre programme en quelque chose de compréhensible et exécutable par l’ordinateur. Un avantage des langages interprété est qu’on peut se servir de l’interpréteur d’une autre façon : plutôt que de lui soumettre le texte de tout un programme, on peut l’utiliser de façon interactive, et ainsi donner directement des instructions écrites en pyhon à l’interpréteur et voir leur effet imméditement. Pour commencer ce cours, on considèrera l’interpréteur python utilisé de façon interactive. Quand on utilise l’interpréteur python de façon interactive, celui-ci nous indique qu’il attend que nous lui soumettions une expression ou une instruction 1 en affichant une invite (ou prompt) : >>> Il suffit alors de saisir une instruction ou une expression, de valider avec la touche entrée, et la valeur de l’expression ou l’effet éventuel de l’instruction s’affichent immédiatement, avant de revenir à l’invite. 3.3. Python est une calculatrice On peut se servir de python comme d’une calculatrice, en utilisant l’interpréteur de façon interactive comme indiqué ci-dessus. Toutefois, on va voir que python sait fait des choses plutôt surprenantes pour une calculatrice. 3.3.1. Nombres Évidemment, python sait manipuler les nombres. Les opérations arithmétiques usuelles !+,-,*,/ sont disponibles. Les parenthèses permettent de grouper les calculs. Par exemple : 1. Nous préciserons plus loin ce qui distingue une expression d’une instruction. 22 3. Les bases ' >>> 4 >>> ... 4 >>> 4 >>> 5 >>> ... 2 >>> -3 >>> 1 $ 2+2 # Ceci est un commentaire 2+2 2+2 # un commentaire sur la m^ eme ligne que du code (50-5*6)/4 # la division entière renvoie la valeur par défaut 7/3 7/-3 7//3 # reste de la division entière de 7 par 3 & % Toutefois ils y a quelques bizarreries dans l’exemple ci-dessus. À plusieurs endroit apparaissent des textes précédés du caractère ’#’. Il est étonnant que python n’aie pas protesté, car a priori il ne comprend rien au français ! En fait, si python n’a rien dit, c’est parce que dès qu’il rencontre le caractère ’#’, il ignore tout ce qui reste jusqu’à la fin de la ligne. Cela permet d’ajouter des commentaires dans les programmes. Ceux-ci sont ignorés par l’interpréteur python, mais ils nous sont bien utiles pour comprendre ce que fait le programme que nous avons sous les yeux. Toutes les calculatrices possèdent une ou plusieurs mémoires, dans lesquelles vous vous pouvez ranger le résultat de calculs pour les réutiliser ensuite dans d’autres calculs. Vous avez déjà tous utilisé cette possibilité. Python aussi possède ce mécanisme. Cela s’appelle des variables. Chaque variable possède un nom. Le signe = permet de ranger une valeur dans une variable (cette opération s’appelle l’assignation). Quand on assigne une valeur à une variable, l’interpréteur n’affiche rien. >>> largeur=20 >>> hauteur=5*9 >>> largeur*hauteur 900 Une valeur peut être assignée simultanément à plusieurs variables. ' >>> >>> 1 >>> 1 >>> 1 $ x = y = z = 1 x y z & % Une variable doit être définie (une valeur doit y être assignée) avant de pouvoir l’utiliser, sinon on obtient une erreur : 23 3. Les bases >>> n # tentative d’accéder àune variable non définie Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name ’n’ is not defined Les nombres à virgule flottante sont acceptés. Si des opérations mélangent des entiers et des nombres à virgule flottante, les entiers sont convertis en nombres à virgule flottante au préalable. >>> 3 * 3.75 / 1.5 7.5 >>> 7.0/2 3.5 Attention ! Si vous écrivez 1/3, le résultat est un entier, car 1 et 3 étant des entiers, Python considère que vous effectuez une division entière, et le résultat sera donc 0 ! Si vous voulez obtenir le nombre à virgule flottante correspondant, il faut écrire 1.0/3.0 3.3.2. Chaı̂nes de caractères En plus des nombres, python sait aussi manipuler les chaı̂nes de caractères (string en anglais). Une chaı̂ne de caractères peut être délimitée soit par des guillemets simples, soit par des guillemets doubles : ' $ >>> ’spam eggs’ ’spam eggs’ >>> ’c\’est chaud’ "c’est chaud !" >>> "c’est chaud !" "c’est chaud !" >>> ’"Oui," dit-il’ ’"Oui," dit-il’ >>> "\"Oui,\" dit-il" ’"Oui," dit-il’ & % Bien entendu, on peut stocker une chaı̂ne de caractères dans une variable, comme nous l’avons fait pour les nombres : >>> s = "A l’aide !" On peut accéder à une partie d’une chaı̂ne de caractère par un mécanisme d’indiçage, qui permet de désigner le rang (l’indice) des caractères de la chaı̂ne qu’on souhaite retrouver. Par exemple, pour accéder au troisième caractère de la chaı̂ne de caractères de l’exemple précédent, on procèdera ainsi : >>> s[2] "l" Pour accéder à la portion commençant au troisième caractère et finissant au cinquième : >>> s[2:4] "l’a" 24 3. Les bases On peut créer une nouvelle chaı̂ne en juxtaposant deux chaı̂nes existantes en utilisant l’opérateur + (opérateur de concaténation) : >>> s + " Je me noie !" "A l’aide ! Je me noie !" Les chaı̂nes sont ce qu’on appelle des objets non mutables. Cela signifie qu’on ne peut pas changer leur contenu. Essayer d’assigner un caractère à un emplacement indicé d’une chaı̂ne de caractère génère une erreur : >>> s[9] = ’?’ ... TypeError: ’str’ object does not support item assignment Si vous avez besoin de modifier une chaı̂ne de caractères, il fut en créer une nouvelle : >>> s[0:8] + ’?’ "A l’aide ?" Pour finir ce premier examen des chaı̂nes de caractère, il existe une fonction prédéfinie len() qui renvoie la longueur d’une chaı̂ne : >>> len(s) 10 3.3.3. Listes Python définit un certain nombre de types de données composés, c’est à dire permettant de regrouper plusieurs valeurs. Un type très utile et très souple à utiliser est le type liste. Une liste est une collection de de valeurs. Ces valeurs peuvent être de différents types, même si habituellement tous les éléments d’une liste sont d’un même type. Pour définir une liste, il suffit d’écrire ses éléments entre [], en les séparant par des virgules : >>> carres = [1, 2, 4, 9, 16, 25] >>> carres [1, 2, 4, 9, 16, 25] Les listes peuvent être indicées, comme les chaı̂nes de caractères : >>> carres[0] #avec un indice, on obtient l’élément 1 >>> carres[1:3] #avec un intervalle d’indices, on obtient une liste [2,4,9] De même, on peut créer une nouvelle liste en concaténant deux listes : >>> carres + [36, 49, 64, 81, 100] [1, 2, 4, 9, 16, 25, 36, 49, 64, 81, 100] Au contraire des chaı̂nes de caractères, les listes sont des objets mutables, c’est à dire qu’on peut modifier leur contenu : 25 3. Les bases ' $ >>> cubes = [1,8,27,65,125] #il y a quelque chose qui cloche >>> 4 ** 3 #le cube de 4 est 64, pas 65 ! 64 >>> cubes[3] = 64 #on remplace la valeur erronée >>> cubes [1,8,27,64,125] & On peut aussi ajouter des éléments à la fin d’une liste avec la méthode 2 append(). >>> >>> >>> [1, % cubes.append(216)#ajoute le cube de 6 cubes.append(7**3)#ajoute le cube de 7 cubes 8, 27, 64, 125, 216, 343] L’assignation permet de changer des morceaux de listes, et éventuellement de changer sa longueur. ' $ >>> lettres = [’a’, ’b’, ’c’, ’d’, ’e’, ’f’, ’g’] >>> lettres [’a’, ’b’, ’c’, ’d’, ’e’, ’f’, ’g’] >>> lettres[2:5] = [’C’, ’D’, ’E’] >>> lettres [’a’, ’b’, ’C’, ’D’, ’E’, ’f’, ’g’] >>> # maintenant on les enlève >>> lettres[2:5] = [] >>> lettres [’a’, ’b’, ’f’, ’g’] & La fonction len() s’applique aussi aux listes. % >>> lettres = [’a’, ’b’, ’c’, ’d’] >>> len(lettres) 4 3.3.4. Premiers pas vers la programmation. L’intérêt de Python ne se limite pas à ce que nous venons de voir. Jusqu’à présent, nous nous sommes contentés d’utiliser python comme d’une calculatrice, mais Python permet de traiter des problèmes beaucoup plus compliqués. Par exemple, voici un petit programme qui calcule et affiche les premiers éléments de la suite de Fibonacci. Pour rappel, la suite de Fibonacci est définie par : f0 = 0, f1 = 1, fi = fi−1 + fi−2 , pour i ≥ 2. 2. Nous verrons plus loin ce qu’est une méthode. 26 3. Les bases ' $ >>> a = 0 >>> b = 1 >>> while b < 10: ... print(b) ... a = b ... b = a + b ... 1 12 3 5 8 & % Cet exemple montre de nouvelles possibilités de Python : – L’instruction while est ce qu’on appelle une boucle. Elle permet de répéter un ensemble d’instructions tant qu’une condition (ici b < 10) est vraie. – Le corps de la boucle, c’est à dire les instructions qui vont être répétées, sont indentées. C’est la façon qu’a Python de désigner un ensemble d’instructions. Il vous faudra être vigilants lors de la rédaction de programmes à indenter proprement les instructions, sinon votre programme signifiera tout autre chose que ce que vous pensiez écrire. – La fonction print() écrit la valeur des arguments qu’on lui passe en paramètre. Elle diffère de l’écriture des expressions réalisée par l’interpréteur et que nous avons vue précédemment par sa façon de traiter les différentes valeurs. Par exemple, les chaı̂nes de caractères sont écrites sans les guillemets. De plus, la fonction print() insère un espace entre chacun des arguments à afficher. Cela permet de formater les résultats de façon agréable : >>> i = 256*256 >>> print(’la valeur de i est’,i) la valeur de i est 65536 Notez qu’en TD nous verrons une autre façon d’afficher un résultat utilisant le système de fenêtrage du système d’exploitation. Nous allons parler de la façon d’acquérir et de restituer des données dans le chapitre suivant. 27 4. Entrées et sorties Il existes diverses façons de restituer la sortie d’un programme. On peut l’écrire à l’écran de façon lisible pour les humains, l’écrire dans un fichier, etc. De même, l’acquisition des données en entrée d’un programme peut se faire de plusieurs façons différentes : le programme peut demander à l’utilisateur de saisir des données avec le clavier ou la souris (ou d’autres dispositifs. . .), il peut lire des données dans un fichier, etc. Nous aborderons ici uniquement l’entrée utilisant le clavier et la souris (de façon très simple), et la sortie à l’écran. Les lecteurs désireux d’approfondir le sujet peut aller consulter la section Input and Output de l’excellent tutoriel Python disponible sur le site officiel Python. 4.1. La sortie Nous avons déjà vu, à la fin de la section 3.3.4, une fonction simple d’affichage : print(). Cette fonction affiche les paramètres qu’on lui passe, les uns à la suite des autres, en insérant un espace entre la valeur de chaque paramètre. La sortie de cet affichage est dirigée vers la console, c’est à dire la sortie par éfaut de l’interpréteur Python. Ce n’est pas très joli je vous l’accorde. Nous allons voir comment afficher des messages dans une fenêtre. 4.1.1. Utiliser un module Les fonctions d’entrée-sortie que nous utiliserons en TD sont regroupées dans un module. Un module est un ensemble de fonctions regroupées ensemble et mises à disposition des programmeurs. Python est livré avec de nombreux modules. Chaque module possède un nom. Avant d’utiliser un module, il faut le déclarer. Le module que nous voulons utiliser s’apelle easygui. >>> import easygui Dorénavent, on peut utiliser ce module. 4.1.2. Affichage simple Pour afficher un texte court, on utilise la fonction easygui.msgbox(). Par exemple : >>> import easygui >>> easygui.msgbox("Bonjour tout le monde !") L’exécution de la deuxième ligne ouvre une boite de dialogue : On peut aussi afficher un message sur plusieurs lignes en utilisant le caractère de fin de ligne ’\n’ : 28 4. Entrées et sorties >>> import easygui >>> easygui.msgbox("Bonjour tout le monde !\n Ca va ?") Vous pouvez aussi afficher le contenu de variables : >>> import easygui >>> a = 30 >>> easygui.msgbox("a = "+ str(a)) La fonction str() transforme un objet quelconque en une chaı̂ne de caractères, qu’on concatène au début du message. 4.2. L’entrée 4.2.1. Entrée d’entiers Pour demander à l’utilisateur de saisir un nombre entier, on utilise la fonction easygui.integerbox (). Cette fonction renverra la valeur saisie par l’utilisateur. Si l’utilisateur essaye de saisir autre chose qu’un entier, un message d’erreur est affiché, et la boite de saisie est réaffichée. Si l’utilisateur annule la saisie, c’est la valeur None qui est renvoyée. Par exemple : >>> import easygui >>> a = easygui.integerbox(’Saisissez un entier’) >>> easygui.msgbox("a = "+ str(a)) La figure 4.1 vous montre le déroulement de ce petit programme en fonction de l’action de l’utilisateur. 4.2.2. Entrée de chaı̂ne de caractère Pour donner à l’utilisateur la possibilité de saisir du texte, on utilise la fonction easygui.enterbox (). Cette fonction renverra la valeur saisie par l’utilisateur. Si l’utilisateur annule la saisie, c’est la valeur None qui est renvoyée. Par exemple : >>> import easygui >>> a = easygui.enterbox(’Saisissez un texte’) >>> easygui.msgbox(’a = "’+ str(a)+ ’"’) La figure 4.2 vous montre le déroulement de ce petit programme en fonction de l’action de l’utilisateur. Une petite remarque sur ce programme : pourquoi avoir utilisé la fonction str(), qui transforme 29 4. Entrées et sorties (a) Saisie d’un entier. (b) Annulation de la saisie. Figure 4.1.: Saisie et affichage d’un entier. un objet en chaı̂ne de caractères, puisque la variable a contient déjà une cha$ine de caractères ? La réponse est que si l’utilisateur annule la saisie, la valeur contenue dans a sera None, qui n’est pas une chaı̂ne de caractères. . . 30 4. Entrées et sorties (a) Saisie d’un texte. (b) Annulation de la saisie. Figure 4.2.: Saisie et affichage d’un texte. 31 5. Les structures de contrôle À la fin de la section 3.3.4, nous avons abordé de façon informelle une instruction particulière : l’instruction while. Cette instruction fait partie de la famille des structures de contrôle. Avant de découvrir cette instruction, toutes les instructions que nous avons abordées s’exécutaient les unes après les autres, autrement dit de façon séquentielle. L’instruction while permet de briser cette séquence en répétant une séquence d’instructions tant qu’une certaine condition est vérifiée. C’est ce qu’on appelle une boucle. Les structures de contrôle se divisent en deux catégories : – les boucles (dont while fait partie) – les alternatives, qui permettent de faire un choix selon certaines conditions. Après un petit retour sur la boucle while et une présentation détaillée de la notion de condition, ce chapitre sera consacré à la découverte d’autres structures de contrôle. 5.1. L’instruction while et la notion de condition Reprenons notre petit programme qui affiche les premiers termes de la suite de Fibonacci. ' >>> a = 0 >>> b = 1 >>> while b < 10: ... print(b) ... a = b ... b = a + b ... 1 12 3 5 8 & $ % La ligne while b < 10: indique le début de la boucle. on peut la traduire par tant que b est inférieur à 10, exécuter les lignes suivantes qui sont indentées . la partie de la ligne b < 10 est la condition. une condition est une expression qui produit un résultat vrai ou faux 1 . 5.1.1. Conditions simples Une condition simple est une expression qui fait appel à un seul opérateur de comparaison (comme dans b < 10) ou à une fonction qui renvoie un résultat vrai ou faux. Suivant les types de données utilisés (nombre, chaı̂ne de caractère, liste. . .), différents opérateurs de comparaison sont disponibles. Nous n’en citerons que quelques uns. 1. En réalité, en Python, une condition est quelque chose de beaucoup plus vaste, mais par souci de simplicité, nous nous limiterons à cette définition, ce qui ne restreint en rien les possibilités de programmation. 32 5. Les structures de contrôle notation Python == notation mathématique = != < 6= < > > <= ≤ >= ≥ signification égalité. Une expression utilisant cet opérateur est vraie si les deux termes sont égaux. Voir les mises en garde ci-dessous. différence. Vraie si les deux termes sont différents inférieur. Vrai si le premier terme est strictement inférieur au second. supérieur. Vrai si le premier terme est strictement supérieur au second. inférieur ou égal. Vrai si le premier terme est inférieur ou égal au second. supérieur ou égal. Vrai si le premier terme est supérieur ou égal au second. Table 5.1.: Les opérateurs de comparaison arithmétiques. 5.1.1.1. Comparaison de nombres Concernant les nombres (entiers ou à virgule flottante), on retrouve les opérateurs de comparaison classiques qui sont résumés dans la table 5.1. Quelques remarques à propos de ces opérateurs : – l’opérateur d’égalité est noté == pour ne pas le confondre avec l’opérateur d’affectation =. – l’opérateur == peut être dangereux avec les nombres à virgule flottante s’il n’est pas utilisé avec précaution. En effet, les nombres à virgule flottante utilisent une représentation nécessairement imprécise : ce ne sont pas des réels ! Il peut donc se produire des erreurs de troncature qui donnent des résultats parfois surprenants quand on a pas l’habitude. Considérez l’exemple suivant : ' >>> a = 1.0/3.0 >>> a # la représentation de a est tronquée, mais pour l’instant tout va bien 0.3333333333333333 >>> b = a + 0.1 >>> b # tiens... làdéjà, c’est bizarre ... 0.43333333333333335 >>> b = b - 0.1 #on devrait retrouver la m^eme valeur que a, mais ... >>> b # ... ce n’est pas ca qui se produit 0.33333333333333337 >>> a == b #catastrophe ! False & Donc, règle immuable : ne jamais faire de comparaison à l’égalité entre deux nombres à virgule flottante ! 5.1.1.2. Comparaison de chaı̂nes de caractères Concernant les chaı̂nes de caractère, on va retrouver les opérateurs de comparaison usuels plus quelques opérateurs spécifiques. Tous ces opérateurs sont regroupés dans la table 5.2. Quelques remarques concernant les opérateurs pourtant sur les chaı̂nes de caractères : – l’égalité entre chaı̂ne est très stricte. Les majuscules ne sont pas égales aux minuscules ! 33 $ % 5. Les structures de contrôle notation Python == notation mathématique = != 6= < et <= <, ≤ > et >= >, ≥ in not in signification égalité. Deux chaı̂nes sont égales si elles contiennent les même caractères exactement. Voir les remarques. différence. Deux chaı̂nes sont différentes si elles diffèrent sur au moins un caractères ou qu’elles n’ont pas la même longueur. Comparaison lexicographique. Vrai si la chaı̂ne de gauche est placée avant celle de droite dans l’ordre du dictionnaire . L’égalité est prise en compte ou non suivant l’opérateur utilisé. Comparaison lexicographique. Vrai si la chaı̂ne de gauche est placée après celle de droite dans l’ordre du dictionnaire . L’égalité est prise en compte ou non suivant l’opérateur utilisé. Opérateur d’appartenance. a in b est vrai si a est une sous-chaı̂ne de b. Voir les exemples en remarque. Inverse de l’opérateur d’appartenance. Table 5.2.: Les opérateurs de comparaison sur les chaı̂nes de caractères. >>> a = "Spam" >>> b = "spam" >>> a == b False Ceci s’explique par le fait que, ce qui est comparé, ce sont les codes numériques associés à chaque caractère. – La remarque précédente a une conséquence sur les opérateurs d’inégalité : >>> a = "Eggs" >>> b = "eggs" >>> a < b True Et oui ! Les lettres majuscules ayant un code plus petit que les lettres minuscules, elles sont placées avant ! De même, les accents peuvent jouer de bien vilains tours : >>> a = "Bete" >>> b = "B^ete" >>> a < b True Pour finir avec les chaı̂nes, voici quelques exemples d’utilisation de l’opérateur in : 34 5. Les structures de contrôle notation Python == notation mathématique = != 6= < et <= <, ≤ > et >= >, ≥ in not in signification égalité. Deux listes sont égales si elles contiennent les même éléments exactement, placées dans le même ordre. Voir les remarques. différence. Deux chaı̂nes sont différentes si elles diffèrent sur au moins un élément ou qu’elles n’ont pas la même longueur. Comparaison lexicographique. Vrai si la liste de gauche est placée avant celle de droite dans l’ordre lexicographique. L’égalité est prise en compte ou non suivant l’opérateur utilisé. Comparaison lexicographique. Vrai si la liste de gauche est placée après celle de droite dans l’ordre lexicographiue. L’égalité est prise en compte ou non suivant l’opérateur utilisé. Opérateur d’appartenance. a in b est vrai si a est un élément de la liste b. Voir les exemples en remarque. Inverse de l’opérateur d’appartenance. Table 5.3.: Opérateurs de comparaison sur les listes. ' $ >>> a = "Spam and eggs" >>> ’m’ in a True >>> ’nd’ in a True >>> ’Egg’ in a False & % 5.1.1.3. Comparaisons de listes Les listes aussi supportent les opérateurs de comparaison. En fait elles supportent les même opérateurs que les chaı̂nes de caractères. Les opérations de comparaison sur les listes sont décrites dans la table 5.3. Quelques remarques sur ces opérateurs. En premier lieu, toutes les comparaisons entre listes se font sur le principe de l’ordre lexicographique. L’ordre lexicographique est une généralisation de l’ordre du dictionnaire à des objets quelconques, autres que des lettres, pourvu que ces objets soient comparables deux à deux. De façon informelle, la comparaison lexicographique entre deux listes a et b fonctionne de la façon suivante : 1. on commence par comparer le premier élément de la liste a avec le premier élément de la liste b. 2. s’ils sont égaux on passe au deuxième, etc... 3. Dès que deux éléments diffèrent, si c’est l’élément de a qui est le plus grand, alors c’est la liste a qui est la plus grande, sinon c’est la liste b. Quelques exemples : 35 5. Les structures de contrôle ' $ >>> a = [1, 4, 9] >>> b = [1, 4, 9] >>> a == b True >>> a > b False >>> a.append(15) >>> a [1, 4, 9, 15] >>> a == b False >>> a > b True >>> c = [1, 9, 9] >>> a == c False >>> a < c True & % L’opérateur in ne présente pas de difficulté particulière : ' >>> a = [1, 4, 9, 16] >>> 4 in a True >>> 10 in a False >>> [1, 4] in a False & $ % 5.1.2. Conditions complexes Parfois on est amené à vouloir formuler des conditions pourtant sur plusieurs comparaisons. Un bon exemple introductif est le problème de décider si un nombre x appartient à un intervalle [a, b[. Déterminer si x ∈ [a, b[ c’est décider si a ≤ x < b est vérifié. Autrement dit, on veut que les comparaisons a ≤ x et x < b soient vérifiées simultanément. notez l’usage du mot et dans la phrase précédente. En Python, on pourrait écrire : >>> a <= x and x < b L’opérateur and s’applique à deux conditions, et produit un résultat vrai uniquement si les deux conditions sont vraies simultanément. Dans la série des opérateurs logiques, on trouve aussi l’opérateur or, qui s’applique lui aussi à deux conditions, et qui produit un résultat vrai si au moins une des deux conditions est vraie. Exemple : Si on souhaite déterminer si un nombre x n’appartient pas à l’intervalle [a, b[, il suffit de vérifier qu’une des deux conditions a ≤ x ou x < b n’est as vérifiée, autrement dit qu’une des deux conditions x < a ou b ≤ x est vérifiée. En Python, on écrira donc : >>> x < a or b <= x 36 5. Les structures de contrôle and False True False False False True False True False False True or False True True True True v not v True False False True Table 5.4.: Tables de vérité des opérateurs logiques. Pour clore le chapitre des opérateurs logiques permettant de construire des conditions complexes, l’opérateur not s’applique à une condition. Il produit un résultat vrai si la condition est fausse, et vice versa. Exemple : On pourrait tester la non appartenance de x à [a, b[ en inversant le test d’appartenance vu précédemment à l’aide de l’opérateur not. >>> not (a <= x and x < b) La table 5.4 résume les valeurs de vérité (vrai/faux) de chacun des opérateurs and, or et not. 5.2. L’instruction if 5.2.1. Version simple Alors que l’instruction while nous permet de répéter un certain nombre de fois une séquence d’instructions, l’instruction if permet quand à elle de faire un choix, et de : – n’exécuter une séquence d’instruction que si une certaine condition est satisfaite ; – éventuellement, si la condition est fausse, exécuter une autre séquence d’instructions. Voici un exemple : le programme suivant détecte su un nombre est pair. 1 2 3 4 5 import easygui nb = easygui . integerbox ( " Saisissez un nombre entier : " ) print nb if ( nb % 2) == 0: easygui . msgbox ( " Le nombre " + str ( nb ) + " est pair " ) Une exécution possible de ce programme serait : 1. Importation du module easygui. 2. Ouverture d’une boı̂te de dialogue demandant la saisie d’un nombre entier, récupération de cette valeur. À l’issue de l’exécution de cette ligne, la variable nb contient alors le nombre saisi (ici 46) : 3. On calcule nb % 2 et on compare ce résultat à 0. Ici, l’expression nb % 2 vaut 0, donc le test est vrai, on exécute donc les instructions indentées qui suivent 37 5. Les structures de contrôle 4. Affichage d’une boite de dialogue indiquant que le nombre saisi est pair : Voyons ce qui se serait passé si nous avions saisi un nombre impair : 1. Importation du module easygui. 2. Ouverture d’une boı̂te de dialogue demandant la saisie d’un nombre entier, récupération de cette valeur. À l’issue de l’exécution de cette ligne, la variable nb contient alors le nombre saisi (ici 45). 3. On calcule nb % 2 et on compare ce résultat à 0. Ici, l’expression nb % 2 vaut 1, donc le test est faux. On exécute donc pas les instructions indentées qui suivent. 5.2.2. Version avec alternative En reprenant l’exemple précédent, imaginons qu’on souhaite cette fois afficher un message non seulement dans le cas où le nombre saisi est pair, mais aussi dans le cas où le nombre saisi est impair. Coici ce qu’on aurait pu écrire. 1 2 3 4 5 6 7 8 import easygui nb = easygui . integerbox ( " Saisissez un nombre entier : " ) print nb if ( nb % 2) == 0: easygui . msgbox ( " Le nombre " + str ( nb ) + " est pair " ) else : easygui . msgbox ( " Le nombre " + str ( nb ) + " est impair " ) easygui . msgbox ( " Fin ! " ) Voici ce qui se passe en fonction du nombre saisi. Examinons d’abord le cas pair : 1. Importation du module easygui. 2. Ouverture d’une boı̂te de dialogue demandant la saisie d’un nombre entier, récupération de cette valeur. À l’issue de l’exécution de cette ligne, la variable nb contient alors le nombre saisi (ici 46) : 3. On calcule nb % 2 et on compare ce résultat à 0. Ici, l’expression nb % 2 vaut 0, donc le test est vrai, on exécute donc les instructions indentées qui suivent 4. Affichage d’une boite de dialogue indiquant que le nombre saisi est pair : 38 5. Les structures de contrôle 5. L’instruction suivante n’est plus au même niveau d’indentation, on saute donc à la fin du if et on affiche le message de fin Voyons ce qui se serait passé si nous avions saisi un nombre impair : 1. Importation du module easygui. 2. Ouverture d’une boı̂te de dialogue demandant la saisie d’un nombre entier, récupération de cette valeur. À l’issue de l’exécution de cette ligne, la variable nb contient alors le nombre saisi (ici 45). 3. On calcule nb % 2 et on compare ce résultat à 0. Ici, l’expression nb % 2 vaut 1, donc le test est faux. On exécute donc les instructions indentées qui suivent le mot clé else : eclipse 4. On poursuit avec la suite (et fin) du programme : 5.2.3. version avec plusieurs alternatives 5.3. La boucle for Cette boucle permet de répéter (itérer ) une séquence d’instructions pour chaque élément d’une séquence (liste, chaı̂ne de caractère), dans l’ordre où les éléments apparaissent dans cette séquence. Par exemple : 1 2 3 4 # -* - coding : utf8 -* mots = [ ’ chat ’ , ’ fen ^ e tre ’ , ’d é fenestration ’] for m in mots : print (m , len ( m ) ) L’affichage de ce programme sera : chat 4 fen^ etre 7 défenestration 14 39 5. Les structures de contrôle Si vous souhaitez modifier la séquence sur laquelle vous itérez alors que vous êtes dans la boucle, (par exemple pour dupliquer certains éléments), il est recommandé d’en faire une copie au préalable : 1 2 3 4 5 6 # -* - coding : utf -8 -* mots = [ ’ chat ’ , ’ fen ^ e tre ’ , ’d é fenestration ’] for m in mots [:]: # Boucle sur une copie . if len ( m ) > 8: mots . insert (0 , m ) print ( str ( mots ) ) L’affichage de ce programme sera : [’défenestration’, ’chat’, ’fen^ etre’, ’défenestration’] 5.3.1. La fonction range() Si vous souhaitez itérer sur une séquence de nombres, la fonction prédéfinie range() vous sera utile. Elle génère des progressions arithmétiques : 1 2 3 # -* - coding : utf -8 -* for i in range (5) : print ( i ) Affichage : 0 1 2 3 4 La borne finale ne fait jamais partie de la séquence. range(10) génère 10 valeurs en commençant par 0. On peut changer le nombre par lequel on démarre, ou changer l’incrément entre deux valeurs de la séquence. Exemple : $ ' range(5,10) 5,6,7,8,9 range(0, 10, 3) 0,3,6,9 range(-10, -100, -30) -10,-40,-70 & % Pour itérer sur les indices d’une séquence, on peut combiner range() et len() de la façon suivante : 1 2 3 4 # -* - coding : utf -8 -* a = [ ’ spam ’ , ’ or ’ , ’ eggs ’] for i in range ( len ( a ) ) : print (i , a [ i ]) Ce qui donnera l’affichage suivant : 1 spam 2 and 3 eggs 40 6. Les fonctions 6.1. Déclaration et utilisation Dans la section 5.1, nous avons défini un petit programme permettant de calculer les premiers termes de la suite de Fibonacci, jusqu’à la valeur 10. Si on souhaite afficher plus de termes de cette suite, il faut modifier le programme, et changer la condition de la boucle while. Il serait plus intéressant d’écrire un morceau de programme permettant d’afficher les valeurs de la suite jusqu’à une certaine valeur n. Autrement dit, on pourrait définir une fonction affichant les termes de la suite de Fibonacci jusqu’à la valeur n : 1 2 3 4 5 6 7 8 9 10 11 # -* - coding : utf -8 -* def fib ( n ) : # Ecrit la s é rie de fibonacci jusqu ’à n """ Affiche la suite de Fibonacci jusqu ’à n . """ a, b = 0, 1 while a < n : print (a , end = ’ ’) a, b = b, a+b print () # appelons mantenant la fonction que nosu venons de d é finir fib (2000) L’exécution de ce programme affichera : 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 Revenons plus précisément sur la définition de la fonction : le mot clé def marque le début de la définition de la fonction. Il doit être suivi du nom de la fonction, puis de la liste des paramètres formels entre parenthèses. Les instructions qui forment le corps de la fonction commencent à la ligne suivante, et doivent être indentées. Optionnellement, la première instruction d’une fonction peut être une chaı̂ne de caractères (délimitée par des triples guillemets) constituant la documentation de la fonction (docstring en anglais). Des outils python permettent de générer la documentation des fonctions d’un programme sous la forme de pages WEB, ou sous forme imprimable. Associer une documentation à chaque fonction créée est une bonne habitude, prenez l’habitude de le faire systématiquement. Toutes les assignations faites dans une fonction se feront localement à la fonction, c’est à dire que les variables ainsi définies ne seront connues que dans la fonction. Si on souhaite assigner une valeur à une variable globale (c’est à dire une variable définie au niveau du programme), il faudra indiquer dans la fonction qu’on va modifier une variable globale. Exemple Voici un petit programme qui manipule une variable c. Parle-t-on toujours de la même variable c ? 1 2 3 4 5 6 # -* - coding : utf -8 -* c = 10 def f () : c = 3 41 6. Les fonctions 7 8 9 10 11 12 13 14 15 16 17 18 print ( " f () : c = " , c ) def g () : global c c = 30 print ( " g () : c = " , c ) print ( " ( a ) c = " , c ) f () print ( " ( b ) c = " , c ) g () print ( " ( c ) c = " , c ) L’exécution du programme fournit l’affichage suivant : ' (a) f() (b) g() (c) & c= 10 : c= 3 c= 10 : c= 30 c= 30 $ % Quand la fonction f() assigne une valeur à la variable c, elle fait référence à une variable c locale à f(). aussi, l’affichage de c réalisé à la ligne 16 du programme fournit une valeur identique à l’affichage réalisé à la ligne 14. En revanche, la ligne 18 affiche 30, car la fonction g() a modifié la variable globale c, grace à la définition global c de la ligne 10. 6.2. Valeurs par défaut des paramètres On peut fournir des valeurs par défaut pour certains paramètres d’une fonction. Cela permet d’appeler la fonction avec moins d’arguments que ceux qui sont définis. Par exemple : 1 2 3 4 5 6 7 8 9 10 11 def ask_ok ( prompt , retries =4 , complaint = ’ Oui ou non s \ ’ il vous plait ! ’) : while True : ok = input ( prompt ) if ok in ( ’o ’ , ’ ou ’ , ’ oui ’) : return True if ok in ( ’n ’ , ’ no ’ , ’ non ’) : return False retries = retries - 1 if retries < 0: raise IOError ( ’ utilisateur dissident ! ’) print ( complaint ) Cette fonction peut être appelée de différente façons : 1. en donnant uniquement le paramètre obligatoire : ask_ok(’Voulez vous vraiment quitter ?’) ; 2. en fournissant un des paramètres optionnels : ask_ok(’Écraser le fichier ?’, 2) ; 3. ou encore en fournissant tous les paramètres : ask_ok(’Écraser le fichier ?’, 2, ’Wow ! On a dit oui ou non !’) ; Notez que cet exemple indroduit le mot clé in. Celui-ci sert à tester si une séquence contient une certaine valeur (nous n’avons pas vu ce qu’est une séquence, nous ne le verrons pas. Considérez que c’est une espèce de liste). Remarquez aussi que ask_ok(’Écraser le fichier ?’, ’Wow ! On a dit oui ou non !’), n’est pas un appel correct. Le paramètre retries n’est pas spécifié, or, si on spécifie 42 6. Les fonctions un certain paramètre n, tous les paramètres situés avant n doivent être spécifiés. On peut s’affranchir de cette contrainte grâce au mécanisme des paramètres nommés (keyword arguments). 6.3. Les paramètres nommés Les fonctions peuvent aussi être appelées en utilisant des paramètres nommés, de la forme nom= valeur. Par exemple, considérez la fonction suivante : # -* - coding : utf -8 -* def fonc ( a =1 , b =2 , c =3 , d =4 , e =5) : print ( " a = " ,a , " b = " ,b , " c = " ,c , " d = " ,d , " e = " ,e ) Le mécanisme des paramètres nommés vout permet d’appeler cette fonction de n’importe laquelle des façons suivantes (dans la partie droite du tableau je vous indique la valeur qu’auront les paramètres : appel valeur des paramètres fonc() a=1 b=2 c=3 d=4 e=5 fonc(4) a=4 b=2 c=3 d=4 e=5 fonc(b=8,d=5) a=4 b=8 c=3 d=5 e=5 fonc(b=35,c=48,a=4,e=9) a=4 b=35 c=48 d=4 e=9 43