L'informatique et l'internet ont révolutionné le monde de la diffusion musicale. Grâce à la numérisation et aux connexions à haut débit, chacun peut de nos jours stocker et échanger très facilement de larges volumes de musique. La vente en ligne a détrôné les magasins physiques, les ventes de disques sont en chute libre et les logiciels sont désormais au centre de la diffusion musicale, que ce soit dans les serveurs des diffuseurs, sur nos ordinateurs et sur les baladeurs et smartphones dans nos poches.
Fondé par de jeunes diplômés de l'UCL, la société SoundPlaza compte mettre en place un espace de rencontre pour les musiciens de la région, avec une forte présence sur la Toile. Ils veulent offrir un site de diffusion de musique pour leurs membres et font appel à votre service pour les aider à développer leur infrastructure de gestion de données. Ils désirent, à partir de la liste des chansons disponibles sur leur serveur, assembler automatiquement des albums correspondant à la taille d'un CD. Ils comptent sur vos compétences nouvellement acquises en programmation orientée objets pour définir et exploiter les structures de données nécessaires.
Depuis le début de ce cours, vous avez commencé à manipuler des classes et objets déjà définis (comme les classes str, int, float et Turtle, par exemple). Néanmoins, cette mission marque votre entrée dans le vrai monde de la programmation orientée objets. Cette semaine, vous allez définir vos propres classes et objets.
A l'issue de cette mission, chacun d'entre vous :
La matière relative à cette mission est décrite dans les sections suivantes de la partie Objects du syllabus en ligne:
Les questions à choix multiples de cette mission sont accessibles en ligne depuis https://inginious.info.ucl.ac.be/course/LSINF1101-PYTHON/Session9_QCM
Considérez le code suivant d'une classe Student représentant un étudiant:
class Student : def __init__(self,n) : """ Initialise un nouvel objet de type Student avec un nom n donné. @pre: - @post: Un objet de type Student a été créé avec comme 'name' n, et un score None pour chacun des trois tests 'test1', 'test2' et 'test3' """ # nom de l'étudiant self.name = n # score reçu par l'étudiant sur trois tests # (initialement None car l'étudiant n'a pas encore passé les tests) self.test1 = None self.test2 = None self.test3 = None def average_score(self) : """" Calcul du score moyen que l'étudiant a obtenu sur les 3 tests. @pre: les variables d'instance test1, test2 et test3 contiennent des valeurs de type int @post: retourne la valeur moyenne de ces trois valeurs """ return (self.test1 + self.test2 + self.test3) / 3
Les instructions suivant donnent un exemple de comment cette classe peut être utilisé:
student = Student("Kim") student.test1 = 14.0 student.test2 = 10.5 student.test3 = 12.0 print("Bonjour, " + student.name + ". Vos scores sont:") print(student.test1) print(student.test2) print(student.test3) print("Votre score moyenne est " + str(student.average_score())) student2 = student print("Votre score moyenne est " + str(student2.average_score())) student = None student2 = None
Sur base de ces extraits, donnez des exemples des notions suivantes, en veillant à être précis et rigoureux dans votre formulation:
**Classe, objet (ou instance)**
**Constructeur et méthode d'initialisation**
**Attribut (ou variable d'instance)**
**Méthodes d'instance**
**Référence à un objet et la valeur None**
**Référence à self**
Dans la question précédente, expliquez les notations student.name, student.average_score() et self.name et leur interprétation par Python.
Dans la première question, pour imprimer le détail d'un objet student de la classe Student on devait exécuter la série d'instructions suivante:
print("Bonjour, " + student.name + ". Vos scores sont:") print(student.test1) print(student.test2) print(student.test3) print("Votre score moyenne est " + str(student.average_score()))
Au lieu d'exécuter cette série d'instructions à l'extérieur de la classe, ce serait beaucoup plus facile de pouvoir obtenir le même résultat en écrivant seulement:
print(student)
A cette fin, ajoutez une méthode magique __str__ à la classe Student qui retourne un string du style:
"Bonjour, Kim. Vos scores sont: 14.0 10.5 12.0 Votre score moyenne est 12.166666666666666"
Tester votre code comme suite:
>>> student = Student("Kim") >>> student.test1 = 14.0 >>> student.test2 = 10.5 >>> student.test3 = 12.0 >>> print(student) Bonjour, Kim. Vos scores sont: 14.0 10.5 12.0 Votre score moyenne est 12.666666666
Dans le cadre d'un cours de programmation, un étudiant a développé une classe Pair permettant de manipuler un objet contenant une paire d'entiers. Le code de cette classe Python est repris ci-dessous :
class Pair: """ Une paire d'entiers """ def __init__(self, x=None, y=None): """ @pre - @post crée une paire (a,b) composée de x et y, ou une paire non-initialisée si aucune valeur de x et de y n'est donné lors de l'appel au constructeur """ self.a = x # le premier élément de la paire self.b = y # le second élément de la paire def __str__(self): return str(self.a) + ", " + str(self.b)
Considérons le code Python ci-dessous illustrant une utilisation de la classe Pair:
p0 = Pair() ##1## p1 = Pair(0,0) ##2## p2 = Pair(1,1) ##3## p0.b = 3 ##4## p1.a = 10 ##5## p2.a = p1.a ##6## p2 = p1 ##7##
Expliquez les opérations effectuées par chacune de ces lignes. Pensez à utiliser une représentation graphique des différents objets telle que celle utilisée dans le syllabus ou les transparents en indiquant les valeurs des différentes variables d'instance.
Après avoir exécuté les opérations précédentes, qu'impriment les instructions suivantes?
print(p0) ##7## print(p1) ##8## print(p2) ##9##
Dans cet exercice, il vous est demandé de créer une classe Student.
Les étudiants sont supposés avoir les caractéristiques suivantes:
Nous créerons les instances de Student avec l'instruction suivante:
stu = Student('firstname', 'surname', 'birthday', 'email')
Considérons à nouveau la classe Pair. Dans cette classe, comment feriez-vous pour écrire une méthode opposite:
def opposite(self): """ @pre - @post retourne une nouvelle instance de Pair dont les deux éléments sont les opposés de ceux de cette paire-ci. L'instance appelante reste inchangée. """ # À implémenter
Cette méthode renvoie une nouvelle instance de la classe Pair telle que ses variables d'instance, a et b, valent l'opposé des variables a et b de l'instance qui y fait appel. Voici un exemple de l'utilisation de cette méthode :
p = Pair(10, -2) print(p) # affiche "10, -2" p.a = 12 print(p) # affiche "12, -2" q = p.opposite() print(p) # affiche "12, -2" print(q) # affiche "-12, 2"
Considérez les instructions suivantes qui utilisent la classe Pair vue précédemment:
p1 = Pair(9, 42) p2 = Pair(9, 42); if (p1 == p2) : print("Egaux en 1") ##1## p2 = p1 if (p1 == p2) : print("Egaux en 2") ##2##
Lesquelles des lignes ##1## et/ou ##2## produiront-elles un message?
Ajoutez une méthode __eq__(self, p) à la classe Pair qui compare la valeur de deux paires, de manière à ce que p1 == p2 retournera True à la ligne ##1##. Pensez à gérer également le cas où p == None.
Un étudiant propose ensuite de construire une classe OrderedPair qui utilise la classe Pair définie précédemment et y ajoute un booléen. Ce booléen est utilisé pour indiquer si une instance de la classe OrderedPair est ordonnée ou non: il est vrai lorsque la valeur stockée dans l'attribut a est inférieure ou égale à la valeur stockée dans l'attribut b.
Considérez la classe OrderedPair, qui prend comme variable d'instance un objet de type Pair dont la classe se trouve dans la question précédente. Voici la classe OrderedPair :
class OrderedPair: def __init__(self): self.p = Pair(0, 0) self.ordered = True # vrai si les deux entiers sont ordonnés: p.a <= p.b def set_a(self, n_a): """ @pre - @post donne au premier élément de la paire la valeur n_a """ # À implémenter def set_b(self, n_b): """ @pre - @post donne au second élément de la paire la valeur n_b """ # À implémenter
Le but de l'exercice est d'implémenter les méthodes set_a() et set_b(), sans oublier de mettre à jour ordered en fonction des nouvelles valeurs des nombres dans la paire.
L'objectif principal de cette mission est de définir et d'utiliser trois classes correspondant respectivement à un temps ou une durée (sous la forme heures-minutes-secondes), à une chanson et à un album. Vous utiliserez ensuite ces classes pour réaliser la génération d'albums souhaitée. Vous travaillerez par groupes de deux.
Dans le cadre du prototype à réaliser, les chansons vous sont fournies dans un fichier contenant des lignes de texte:
"TITRE AUTEUR MIN SEC"
où "TITRE", "AUTEUR", "MIN" et "SEC" correspondent respectivement au titre, à l'auteur, au nombre de minutes et de secondes de la chanson, et ne contiennent eux-mêmes pas d'espaces, par exemple:
Let's_Dance David_Bowie 4 5 Relax Frankie_Goes_To_Hollywood 3 54 Purple_Rain Prince 5 48 Enjoy_The_Silence Depeche_Mode 4 13
Ce format facilite leur lecture. On vous rappelle que la méthode split() permet de séparer un string contenant des espaces en mots individuels.
Il est plus que jamais important de bien tester votre code, étant donné que les classes que vous définissez pourront être utilisées dans des usages multiples. Au sein de votre binôme, un étudiant pourra se concentrer sur le développement d'une classe tandis que l'autre préparera des tests. Les spécifications des méthodes étant donnée par avance, il est tout à fait possible de préparer des tests en même temps que (voire avant) la classe à tester. Il s'agit en quelque sorte d'un jeu: le développeur parviendra-t-il à écrire une classe qui fonctionnera correctement sous toutes les attaques du testeur? Le testeur parviendra-t-il à trouver le test qui causera un fonctionnement incorrect? Pour que ces questions aient un sens, il est également primordial de bien spécifier les méthodes.
(Remarque: lors de cette mission on n'utilisera pas encore les 'tests unitaires'; vous pouvez tester votre code comme vous l'avez fait lors des missions précédentes. Les tests unitaires seront introduites dans une mission futur.)
Chargez le fichier "music-db.txt". Ce fichier contient une liste de plus de 300 chansons, au format décrit ci-dessus.
Créez une nouvelle classe Duree qui représente une durée exprimée en heures (h), minutes (m) et secondes (s).
Créez une série de tests que vous utiliserez pour tester la classe Duree. Ajoutez des tests au fur et à mesure que vous ajouterez des méthodes à la classe Duree. Il est une bonne idée d'avoir un ou plusieurs tests, avec différents scénarios de test, par méthode de la classe Duree. Par exemple, si votre classe Duree a une méthode ajouter(), vous pourrez implémenter une fonction test_ajouter() pour tester les différents scénarios d'utilisation de la méthode ajouter() de la classe Duree.
Dans la classe Duree, définissez une méthode d'initialisation avec comme paramètres __init__(self, h, m, s), qui requiert que m et s soient dans l'intervalle [0..60[ et crée un nouveau temps de h heures, m minutes et s secondes. Veillez à la spécification et vérifiez que les conditions requises soient respectées.
Dans la classe Duree, ajoutez des méthodes correspondant aux spécifications suivantes:
def toSecondes(self) : """ Retourne le nombre total de secondes de cette instance de Duree (self). """ def delta(self,d) : """ Retourne la différence entre cette instance de Duree (self) et la Duree d passée en paramètre, en secondes (positif si ce temps-ci est plus grand). """ def apres(self,d): """ Retourne True si cette instance de Duree (self) est plus grand que la Duree d passée en paramètre; retourne False sinon. """ def ajouter(self,d): """ Ajoute une autre Duree d à cette instance de Duree (self). Corrige de manière à ce que les minutes et les secondes soient dans l'intervalle [0..60[, en reportant au besoin les valeurs hors limites sur les unités supérieures (60 secondes = 1 minute, 60 minutes = 1 heure). """ def __str__(self): """ Retourne cette durée sous la forme de texte "heures:minutes:secondes". Astuce: la méthode "{:02}:{:02}:{:02}".format(heures, minutes, secondes) retourne le String désiré avec les nombres en deux chiffres en ajoutant les zéros nécessaires. """
Ecrivez des fonctions test pour chacune de ces méthodes de la classe Duree, exécutez-les et assurez-vous que les résultats sont conformes aux spécifications, en corrigeant au besoin.
Créez une nouvelle classe Chanson représentant une chanson, caractérisée par un titre t (string), un auteur a (string) et une durée d (Duree). Définissez une méthode d'initialisation avec comme paramètres __init__(self, t, a, d).
Dans la classe Chanson, implémentez une méthode de conversion __str__ correspondant à la description suivante:
def __str__(self): """ Retourne un String décrivant cette chanson sous le format "TITRE - AUTEUR - DUREE". Par exemple: "Let's_Dance - David_Bowie - 00:04:05" """
Créez une série de tests que vous utiliserez pour tester la classe Chanson.
Créez une classe Album représentant un album contenant une ou plusieurs chansons.
En plus d'une méthode d'initialisation pour créer un album vide, cette classe contient une méthode add(self,chanson) pour ajouter une chanson à un album. Cette méthode retourne False si lors de l'ajout d'une chanson l'album a atteint 100 chansons ou la durée dépasserait 75 minutes. Sinon la chanson est rajoutée et la méthode add retourne True.
Dans la classe Album, implémentez également une méthode de conversion __str__ pour imprimer la description d'un album selon le format suivant:
Album 21 (12 chansons, 00:47:11) 01: White_Wedding - Billy_Idol - 00:04:12 02: Stand_And_Deliver - Adam_&_The_Ants - 00:03:33 03: You_Spin_Me_Around - Dead_Or_Alive - 00:03:14 04: Wired_For_Sound - Cliff_Richard - 00:03:38 05: Some_Like_It_Hot - The_Power_Station - 00:03:45 06: 99_Luftballons - Nena - 00:03:50 07: Keep_On_Loving_You - Reo_Speedwagon - 00:03:22 08: Seven_Into_The_Sea - In_Tua_Nua - 00:03:51 09: Love_Is_A_Battlefield - Pat_Benatar - 00:05:20 10: Etienne - Guesch_Patti - 00:04:07 11: This_Is_Not_A_Love_Song - Public_Image_Limited - 00:04:12 12: Love_Missile_F1-11 - Sigue_Sigue_Sputnik - 00:04:07
Créez une série de tests pour tester la classe Album.
Finalement, complétez votre programme par une série d'instructions qui:
- lit dans le fichier "music-db.txt", ligne par ligne, des descriptifs de chanson au format décrit en tête de cette section;
- pour chaque ligne lue, construite une instance de Chanson;
- stocke ses chansons dans un album;
- lorsque le nombre de chansons stockés dans un album a atteint 100 chansons ou la durée dépasserait 75 minutes, imprime à la console un descriptif de cet album avec les chansons accumulées, suivant l'exemple donné ci-dessous;
- poursuit la lecture du fichier et l'ajout des chansons dans un album suivant;
- répète ces étapes jusqu'à ce que le fichier edt vide et imprime le dernier album.
Si tout va bien votre programme final produira l'output suivant
Album 1 (17 chansons, 01:10:55) 01: Let's_Dance - David_Bowie - 00:04:05 02: Relax - Frankie_Goes_To_Hollywood - 00:03:54 03: Purple_Rain - Prince - 00:05:48 04: Enjoy_The_Silence - Depeche_Mode - 00:04:13 05: Chacun_Fait_C'Qui_Lui_Plait - Chagrin_D'amour - 00:04:08 06: Love_Missile_F1-11 - Sigue_Sigue_Sputnik - 00:04:06 07: Spaceman - Babylon_Zoo - 00:03:59 08: Hijo_De_La_Luna - Mecano - 00:04:19 09: 7_Seconds - Youssou_N'Dour_&_Neneh_Cherry - 00:03:48 10: Osez_Josephine - Alain_Bashung - 00:02:57 11: Dejeuner_En_Paix - Stephan_Eicher - 00:03:27 12: New_Gold_Dream - Simple_Minds - 00:05:31 13: Missing - Everything_But_The_Girl - 00:04:44 14: Nineteen - Paul_Hardcastle - 00:03:38 15: Killer - Adamski - 00:04:13 16: Unbelievable - EMF - 00:03:29 17: Overload - Sugababes - 00:04:36 Album 2 (17 chansons, 01:11:24) 01: Ice_Ice_Baby - Vanilla_Ice - 00:04:29 02: Do_You_Really_Want_To_Hurt_Me - Culture_Club - 00:04:23 03: Under_The_Milky_Way - The_Church - 00:04:57 04: Shout - Tears_For_Fears - 00:06:29 05: Pure_Morning - Placebo - 00:04:15 06: Porcelain - Moby - 00:04:00 07: Toi_Mon_Toit - Elli_Medeiros - 00:03:37 08: Just_A_Friend_Of_Mine - Vaya_Con_Dios - 00:03:22 09: Sleeping_Satellite - Tasmin_Archer - 00:04:36 10: I_Won't_Let_You_Down - DPH - 00:04:05 11: A_Girl_Like_You - The_Smithereens - 00:04:36 12: Ready_To_Go - Republica - 00:03:39 13: Oh_Carolina - Shaggy - 00:03:10 14: La_Sonora - Starflam - 00:03:49 15: Tombe_Pour_La_France - etienne_Daho - 00:04:13 16: The_Captain_Of_My_Heart - Double - 00:03:58 17: Human - Human_League - 00:03:46 ... Album 21 (12 chansons, 00:47:11) 01: White_Wedding - Billy_Idol - 00:04:12 02: Stand_And_Deliver - Adam_&_The_Ants - 00:03:33 03: You_Spin_Me_Around - Dead_Or_Alive - 00:03:14 04: Wired_For_Sound - Cliff_Richard - 00:03:38 05: Some_Like_It_Hot - The_Power_Station - 00:03:45 06: 99_Luftballons - Nena - 00:03:50 07: Keep_On_Loving_You - Reo_Speedwagon - 00:03:22 08: Seven_Into_The_Sea - In_Tua_Nua - 00:03:51 09: Love_Is_A_Battlefield - Pat_Benatar - 00:05:20 10: Etienne - Guesch_Patti - 00:04:07 11: This_Is_Not_A_Love_Song - Public_Image_Limited - 00:04:12 12: Love_Missile_F1-11 - Sigue_Sigue_Sputnik - 00:04:07
Pour cette mission, vous devez soumettre votre programme mission8.py, un fichier test.py avec vos fonctions de tests pour les différentes classes que vous avez implémenté et votre fichier README.txt au serveur de soumissions de programmes du cours.
Votre fichier mission8.py doit au moins contenir les classes Duree, Chanson et Album, ainsi qu'une série d'instructions pour lire le fichier et créer et imprimer les albums.
Votre fichier test.py doit au moins contenir des tests pour les différentes méthodes des classes Duree, Chanson et Album, ainsi qu'une série d'instructions pour lancer tous les tests.
L'exécution de votre programme mission8.py doit imprimer l'output ci-dessus sur base du fichier original "music-db.txt". Il ne vous est pas permis de faire des modifications à ce fichier.
Create a new class SMSStore. The class will instantiate SMS_store objects, which can store SMS messages, similar to an inbox or outbox on a cellphone:
my_inbox = SMS_store()
This store can hold multiple SMS messages (its internal state will just be a list of messages). Each message will be represented as a tuple: (has_been_viewed, from_number, time_arrived, text_of_SMS)
The inbox object should provide these methods:
my_inbox.add_new_arrival(from_number, time_arrived, text_of_SMS) # Makes a new SMS tuple, and inserts it after the other messages # in the store. When creating this message, # its has_been_viewed status is set False. my_inbox.message_count() # Returns the number of sms messages in my_inbox my_inbox.get_unread_indexes() # Returns list of indexes of all not-yet-viewed SMS messages my_inbox.get_message(i) # Return (from_number, time_arrived, text_of_sms) for message[i] # Also change its state to "has been viewed". # If there is no message at position i, return None my_inbox.delete(i) # Delete the message at index i my_inbox.clear() # Delete all messages from inbox Write the class ``SMS_store``, create a message store object, write tests for each of the above methods, and implement the methods.
Dans cet exercice, il vous est demandé de créer une fonction créant des instances de la classe Student basées sur les informations contenus dans un fichier, et les renvoyant dans une liste. Ceci afin de récupérer les notes de l'interros des différents participants.
Voici le constructeur de la classe Student:
class Student: def __init__(self, firstname, surname, mark): self.firstname = firstname self.surname = surname self.mark = mark
Le fichier suit le format suivant:
Kim Mens 12 Charles Pecheur 13 Siegfried Nijssen 18
Implémentez la fonction marks_from_file(filename) qui lit, ligne par ligne, le fichier nommé filename, et qui retourne une liste d'instances de Student, correspondant aux différents lignes du fichier.
Le traitement de données commerciales et financières est un des champs majeurs d'application de l'informatique. De nos jours, la moindre PME utilise l'informatique pour gérer sa comptabilité, ses commandes et sa facturation, et l'infrastructure informatique est devenue un élément critique de toute grande entreprise, dont la supervision représente un poste clé au sein de l'exécutif, celui de Chief Information Officer ou CIO.
PCLine s.p.r.l., une société de vente de matériel et de service informatique locale de Louvain-la-Neuve, fait appel à vos services pour développer ses applications informatisées de facturation. Les gérants de PCLine disposent déjà d'un programme pour imprimer leurs factures et calculer la TVA et les totaux. Ils désirent développer également la livraison des articles et de pièces, en intégrant l'impression des bordereaux de livraison dans leur logiciel de facturation existant, sans devoir écrire complètement leur logiciel existant. Heureusement, leur logiciel étant écrit en Python dans un style orienté objets, leur consultant de l'UCL leur a confirmé qu'il était possible de l'étendre sans le modifier au moyen des mécanismes d'héritage de Python. Ils ont donc confié aux étudiants de 1er Bac la tâche d'ébaucher la représentation des pièces en catalogue, leur intégration dans la facturation, ainsi que l'impression des bordereaux de livraison en plus de l'impression des factures.
A l'issue de cette mission, chacun d'entre vous :
pourra expliquer en ses propre mots et illustrer par l'exemple les principes de l'héritage en Python:
- l'héritage d'une classe, la redéfinition de méthodes;
- les notions d'héritage, de polymorphisme, et d'hiérarchie de classes;
- l'utilisation de self et super();
sera capable d'utiliser l'héritage pour étendre un programme Python;
pourra rendre privé les variables d'instance d'une classe et écrire des méthodes pour y accéder;
aura appris les notions de variables et méthodes de classe et leur utilité;
pourra utiliser les méthodes magiques comme :
- __init__ pour initialiser les objets d'une classe;
- __str__ pour retourner une représentation textuelle d'un instance d'une classe;
- __eq__ pour définir l'égalité entres objets d'une classe;
La matière relative à cette mission est décrite dans les sections suivantes de la partie Objects du syllabus en ligne:
ainsi que les annexes:
Les questions à choix multiples de cette mission sont accessibles en ligne depuis https://inginious.info.ucl.ac.be/course/LSINF1101-PYTHON/Session10_QCM
L'héritage est un principe de base de la programmation orientée objet. Considérons les classes A, B, C et D ci-dessous :
class A : def m1(self) : print("A 1") def m2(self) : print("A 2") def m3(self) : self.m1() # appel à la méthode m1 sur la même instance def nom(self) : return "A" class B(A) : def m2(self): print("B 2") class C(A): def m1(self) : print("C 1") def nom(self): return "C" class D(C) : def m2(self) : print("D 2")
Considérant ces quatre classes, on vous demande de :
a = A() print(a.nom()) a.m1() a.m2() a.m3() b = B() print(b.nom()) b.m1() b.m2() b.m3() c = C() print(c.nom()) c.m1() c.m2() c.m3() d = D() print(d.nom()) d.m1() d.m2() d.m3()
class E : def m(self) : print("E 1") def n(self) : print("E 2") def p(self) : self.n() # appel à la méthode m1 sur la même instance class F(E) : def q(self) : print("F 1") def n(self) : super().n() # appeler la méthode définie sur la classe mère print("F 2") def r(self) : self.m() # appel à la méthode m1 sur la même instance
Expliquer ce qui sera affiché lors de l'exécution des instructions Python suivantes :
f = F() f.q() f.m() f.r() f.n() f.p()
Considérons la classe Figure reprise ci-dessous :
class Figure: def __init__(self,x,y,visible=False) : """ @pre x, y sont des entiers représentant des positions sur l'écran @post Une figure a été créée avec centre de gravité aux coordonnées x,y. Cette figure n'est initialement pas visible. """ self.x = x self.y = y self.__visible = visible def estVisible(self) : """ @pre - @post a retourné la visibilité de cette figure """ return self.__visible def surface(self) : """ @pre - @post la surface (un float) de la figure a été calculé et retournée """ pass # code non fourni
Cette classe Figure est la classe mère d'un ensemble de classes permettant de représenter des figures géométriques. Chaque figure géométrique est placée à une position (x,y) (centre de gravité) sur l'écran et la classe contient des variables d'instance et des méthodes permettant de manipuler cette figure géométrique (notamment des méthodes permettant d'afficher la figure à l'écran, mais ces méthodes ne sont pas reprises dans les extraits présentés dans cet exercice). Parmi ces figures géométriques, on trouve notamment la classe Rectangle qui hérite de la classe Figure et dont un fragment est repris ci-dessous :
class Rectangle(Figure): def __init__(self,lo,la,x,y) : """ @pre lo (longeur) et la (largeur) sont des entiers positifs x, y sont des entiers représentant des positions sur l'écran @post un rectangle dont le centre de gravite est en x,y et ayant comme longueur lo et comme largeur la a été créé """ super().__init__(x,y) self.longeur = lo self.largeur = la def __str__(self) : return str((self.longeur,self.largeur,self.x,self.y,self.estVisible())) >>> r = Rectangle(10,20,0,0) >>> print(r) (10, 20, 0, 0, False)
Maintenant expliquez :
Dans la classe Rectangle, faut-il redéfinir les méthodes suivantes (si oui, écrivez le code de la nouvelle méthode - si non, expliquez pourquoi ):
Pour éviter de rendre accessible les variables x, y et visible à l'extérieur d'une Figure, quelqu'un modifie le code de la classe Figure. Concrètement, pour rendre ces variables privés, on ajoute un __ à leur nom, comme suit :
class Figure: def __init__(self,x,y,visible=False) : """ @pre x, y sont des entiers représentant des positions sur l'écran @post Une figure a été créée avec centre de gravité aux coordonnées x,y. Cette figure n'est initialement pas visible """ self.__x = x self.__y = y self.__visible = visible def estVisible(self) : """ @pre - @post a retourné la visibilité de cette figure """ return self.__visible def surface(self): """ @pre - @post la surface (un float) de la figure a été calculé et retournée """ pass # code non fourni
Malheureusement, le code de la classe Rectangle, qui hérite de la classe Figure ne marche plus maintenant. Quel est le problème?
Comment peut-on corriger ce problème? Corrigez le code afin que les instructions suivantes produisent le résultat voulu.
>>> r = Rectangle(10,20,0,0) >>> print(r) (10,20,0,0,False)
Comment feriez-vous maintenant pour définir une classe Carre qui étend la classe Rectangle et permet de représenter un carré ?
Définissez une méthode __eq__ pour la classe Figure, telle que deux figures sont égales si leur surface est égale.
Que se passe-t il si on veut comparer deux rectangles ayant la même surface? Par exemple:
>>> r1 = Rectangle(10,40,0,0) >>> r2 = Rectangle(2,200,0,0) >>> r1 == r2
Que se passe-t il si on veut comparer un rectangle avec un carré ayant la même surface? Par exemple:
>>> r = Rectangle(10,40,0,0) >>> c = Carre(20,0,0) >>> print(r == c)
Complétez la classe Ticket ci-dessous:
""" Un ticket de parking """ class Ticket : __prochainnumero = 1 # variable de classe pour générer le numéro du ticket def __init__(self) : """ @pre - @post Crée un ticket avec un nouveau numéro. Les numéros sont attribués séquentiellement à partir de 1. """ # A COMPLETER def numero(self): """ @pre - @post retourne le numero de billet """ return self.__numero
Pour cette mission, vous disposez au départ d'un programme simple permettant d'imprimer des factures. Ce programme comporte principalement deux classes: l'une représente un Article, c'est-à-dire une ligne dans la facture; l'autre représente une Facture sous forme d'une liste d'articles et offre des méthodes permettant de l'imprimer.
Une classe de test initiale Test est également fournie, elle produit l'exemple de facture suivant:
Facture PC Store - 22 novembre =================================================================================== | Description | prix HTVA | TVA | prix TVAC | =================================================================================== | laptop 15" 8GB RAM | 743.79 | 156.20 | 899.99 | | installation windows | 66.11 | 13.88 | 79.99 | | installation wifi | 45.22 | 9.50 | 54.72 | | carte graphique | 119.49 | 25.09 | 144.58 | =================================================================================== | T O T A L | 974.61 | 204.67 | 1179.28 | ===================================================================================
Une version plus étendue de cette classe de test est également fournie; elle vous permettra de tester les extensions que vous devez développer.
Votre objectif principal est de développer des classes qui étendent Article , en offrant des fonctionnalités supplémentaires tout en restant utilisables par la classe Facture . Dans un deuxième temps, vous ajouterez aussi une méthode à la classe Facture qui exploite ces nouvelles fonctionnalités. Votre programme complet devrait être capable de calculer et générer des factures et bons de livraison tels que celui-ci:
Facture No 1 : Facture PC Store - 22 novembre =================================================================================== | Description | prix HTVA | TVA | prix TVAC | =================================================================================== | laptop 15" 8GB RAM | 743.79 | 156.20 | 899.99 | | installation windows | 66.11 | 13.88 | 79.99 | | installation wifi | 45.22 | 9.50 | 54.72 | | carte graphique | 119.49 | 25.09 | 144.58 | | 1 * disque dur 350 GB @ 49.99 | 49.99 | 10.50 | 60.49 | | 3 * souris bluetooth @ 15.99 | 47.97 | 10.07 | 58.04 | | Réparation (0.75 heures) | 46.25 | 9.71 | 55.96 | | 5 * adaptateur DVI - VGA @ 12.00 | 60.00 | 12.60 | 72.60 | | 2 * Java in a Nutshell @ 24.00 | 48.00 | 2.88 | 50.88 | | 5 * souris bluetooth @ 15.99 | 79.95 | 16.79 | 96.74 | =================================================================================== | T O T A L | 1306.77 | 267.22 | 1573.99 | =================================================================================== Livraison - Facture No 1 : PC store 22 octobre =================================================================================== | Description | poids/pce | nombre | poids | =================================================================================== | disque dur 350 GB (!) | 0.355kg | 1 | 0.355kg | | souris bluetooth | 0.176kg | 3 | 0.528kg | | adaptateur DVI - VGA | 0.000kg | 5 | 0.000kg | | Java in a Nutshell | 0.321kg | 2 | 0.642kg | | souris bluetooth | 0.176kg | 5 | 0.880kg | =================================================================================== | 5 articles | | 16 | 2.405kg | =================================================================================== (!) *** livraison fragile ***
Voici les étapes à suivre:
Chargez les fichiers et étudiez leurs contenus. Vous disposez des fichiers suivants:
Vous devrez vous-mêmes créer au moins deux classes ArticleReparation et ArticlePiece qui étendent Article, une classe Piece , ainsi que deux méthodes supplémentaires dans Facture , selon les instructions qui suivent.
Créez une nouvelle classe "Piece" qui représente une pièce que l'on peut facturer. Elle comporte les données suivantes:
- une description (string), p.ex. 'souris bluetooth';
- un prix unitaire (float), p.ex. 15.99 Euro;
- un poids unitaire en kg (float), p.ex. 0,154 kg;
- un indicateur booléen indiquant si la pièce est fragile, p.ex. un disque dur est fragile mais pas une souris;
- un indicateur booléen indiquant si la pièce est à taux de TVA réduit, p.ex. les livres bénéficient de TVA réduite.
Ajoutez une méthode d'initialisation permettant d'initialiser toutes ces données. Cette méthode d'initialisation doit aussi être utilisable avec seulement deux paramètres (description, prix) pour les pièces de poids négligeable, non fragiles et à taux de TVA normal.
Ajoutez des méthodes accèsseurs (méthodes description() , prix() , poids() , fragile() , tva_reduit()) pour toutes ces données.
Ajoutez une méthode magique __eq__ afin que deux pièces sont considérées égales ( == ) si elles ont la même description et le même prix (les autres données sont ignorées pour la comparaison).
Dans la classe Facture, ajoutez une méthode printLivraison() qui imprime un bordereau de livraison comme
Livraison - Facture No 1 : PC store 22 octobre =================================================================================== | Description | poids/pce | nombre | poids | =================================================================================== | disque dur 350 GB (!) | 0.355kg | 1 | 0.355kg | | souris bluetooth | 0.176kg | 3 | 0.528kg | | adaptateur DVI - VGA | 0.000kg | 5 | 0.000kg | | Java in a Nutshell | 0.321kg | 2 | 0.642kg | | souris bluetooth | 0.176kg | 5 | 0.880kg | =================================================================================== | 5 articles | | 16 | 2.405kg | =================================================================================== (!) *** livraison fragile ***
Ce bordereau: * imprime une en-tête avec la description de la facture; * imprime toutes les pièces dans la facture avec, pour chacune, sa description, son poids unitaire, le nombre facturé et le poids correspondant; * ajoute une marque dans la description des pièces fragiles; * totalise et imprime à la fin le nombre d'articles, le nombre de pièces et le poids total; * imprime un message supplémentaire si (et seulement si) la livraison contient une ou plusieurs pièces fragiles.
Remarquez que les détails imprimés dans ce bordereau de livraison ne concernent que les articles de type ArticlePiece; les autres articles sont ignorés. Pour faciliter le formatage du texte, vous pouvez utiliser la méthode format, déjà utilisée à plusieurs endroits dans la classe Facture. Si vous ne la connaissez pas, n'hésitez pas à chercher en-ligne comment cette méthode format fonctionne exactement. Pour implémenter la méthode printLivraison() , réutiliser un maximum de méthodes déjà existantes dans la classe Facture .
Finalement, modifiez la classe TestFactureInitial ou créez une nouvelle classe teste TestFactureEtape5 pour tester votre nouvelle méthode.
Pour cette mission, vous devez soumettre toutes les classes de votre programme dans un fichier mission9.py, vos classes tests dans un fichier test.py, ainsi que votre fichier README.txt qui décrit comment on peut tester votre code.
Votre fichier mission9.py doit contenir les classes Facture, Article, ArticleReparation, Piece et ArticlePiece.
Votre fichier test.py doit contenir soit votre classe TestFactureInitial modifiée, soit les classes TestFactureInitial, TestFactureEtape1, TestFactureEtape3 et TestFactureEtape5, ainsi qu'une série d'instructions à la fin pour lancer tous les tests automatiquement lors de l'exécution du fichier.
L'exécution de votre programme mission9.py doit imprimer une facture et un bon de livraison comme illustrer plus haut dans ce document.
Il est possible d'ajouter de multiples variantes d'article à ce programme. Par exemple:
On peut également ajouter des calculs supplémentaires sur les factures, par exemple un bilan des frais de main d'oeuvre. Un peu plus difficile, modifier la méthode printLivraison() de la classe Facture pour n'imprimer qu'une seule ligne par pièce qui cumule les articles correspondants, comme dans la méthode nombre() .
Dans cet exercice, il vous est demandé de programmer une classe Student . Chaque instance de cette classe représente un étudiant, ayant les caractéristiques suivantes :
La représentation en format string de cet objet doit être, par exemple : _L'étudiant 27: Hervé Ducobu né le 1 avril, peut être contacté par **herve.ducobu@student.uclouvain.be**_ Pour cela vous êtes invités à implémenter la méthode __str__ .
Pour générer un numéro d'identification unique différent pour chaque étudiant, vous êtes conseillé à utiliser une variable de classe pour retenir le dernier numéro généré. Ce numéro est augmenté lors de chaque nouvelle création d'instance de cette classe. Il est déconseillé d'utiliser une variable globale au lieu d'une variable de classe.
En tant que nouveau manager d'un centre d'expédition d'Amazon, vous devez créer une classe pour représenter les commandes.
Une commande a les attributs suivants :
l'id de l'acheteur ( id_buyer ) l'id de l'objet ( id_item ) la quantité ( quantity ) le prix de l'objet ( price )
Et doit avoir les méthodes suivantes :
Il doit aussi y avoir des méthodes de classe :
get_number_total_command() : retourne le nombre total de commandes effectuées get_total_price() : retourne le prix total de toutes les commandes
Nous allons créer l'objet avec :
command = Command(id_buyer, id_item, quantity_item, price_item)
Commencez par implémenter une classe Animal, qui devrait avoir :
Seul le name de l'animal doit être passé en argument au constructeur. Nous avons décidé de mettre asleep à False par défaut. Evidemment, comme cette classe représente n'importe quel animal, nous ne connaissons pas la valeur que les autres attributs devraient prendre. La chose la plus logique à faire serait donc de les mettre à None.
Vous implémenterez ensuite trois sous-classes différentes d'Animal réprésentant trois espèces différentes : Lion, Owl et Giraffe. Les valeurs des différents attributs de la classe parent devraient maintenant être définies dans les méthodes d'initialisation.
Lion aura une méthode additionnelle roar(), qui affichera ROARRR!!!.
Owl aura une méthode additionnelle fly(), qui ne fera rien (utilisez l'expression pass pour ne rien faire).
Giraffe aura un attribut additionnel neck_length (en mètres). Cette information sera donnée au constructeur de la classe. Notez que votre code ne devrait pas accepter de valeurs invalides pour cet attribut (les tailles négatives ou exprimées en chaines de caractères par exemple). Levez une ValueError dans le cas où vous avez une valeur invalide.
Vous implémenterez ensuite une dernière classe : Zoo , qui aura une liste de tous les animaux appelée animals comme attribut et une méthode add_animal(animal) pour remplir cette liste. Levez une ValueError si cette méthode reçoit quelque chose d'autre qu'un animal.
On vous demande enfin de définir une fonction create_my_zoo() qui retournera votre zoo contenant un lion, une chouette et deux girafes. Ce sont vos animaux, appelez les comme vous voulez ;)
Dans cette mission, vous allez approfondir votre compréhension de concepts importants de la programmation orientée objets déjà abordés dans les deux missions précédentes, ainsi que quelques nouveaux concepts :
- les classes, objets, variables et méthodes instance ;
- les variables et méthodes de classe ;
- les variables et méthodes privés, les méthodes accesseurs et mutateurs ;
- la portée des variables d'instance ;
- l'héritage, la redéfinition et l'écrasement de méthodes, le polymorphisme ;
- la liaison dynamique et les subtilités de la sémantique de self et super().
Vous aurez également l'occasion d'écrire des tests unitaires pour vos programmes orientés objets.
Si vous n'avez pas encore eu le temps de relire en détail les différents chapitres de la partie Objects du syllabus en ligne, maintenant est venu le moment de rattraper votre retard. Le syllabus explique bien différents concepts, sur un autre exemple que les exemples utilisés dans le cours magistral :
On vous conseille également de tester et de jouer avec les différents exemples de code qui se trouvent dans les annexes suivantes, venant soit du syllabus en ligne soit correspondant aux exemples du cours magistral :
Dans cette mission, vous devrez également utiliser le framework unittest pour produire des classes de test. En préparation de cette mission, consultez donc la documentation de ce cadre de test, et essayez de faire quelques classes test simple en utilisant cet approche. Un bon tutoriel (en français) sur les tests unitaires avec unittest se trouve également ici Créez des tests unitaires avec unittest
Les questions à choix multiples de cette mission sont accessibles en ligne depuis https://inginious.info.ucl.ac.be/course/LSINF1101-PYTHON/Session10_2019_QCM
On vous donne le code suivant qui teste certaines fonctionnalités du module random.
import unittest import random class RandomTest(unittest.TestCase): """Clases de test utilisé pour tester les fonctions du module 'random'""" def setUp(self): """Initialisation des tests.""" self.l = list(range(10)) def test_choice(self): """Test de fonctionnement de la fonction random.choice""" e = random.choice(self.l) # Vérifie que 'e' est dans 'l' self.assertIn(e,self.l) def test_shuffle(self): """Test le fonctionnement de la fonction random.shuffle""" random.shuffle(self.l) self.l.sort() self.assertEqual(self.l, list(range(10))) def test_sample(self): """Test le fonctionnement de la fonction random.sample""" extrait = random.sample(self.l, 5) for element in extrait: self.assertIn(element,self.l) self.assertRaises(ValueError, random.sample, self.l, 20) # To automatically launch this test when executing this file if __name__ == '__main__': unittest.main()
Considérant cette classe, on vous demande de :
Expliquez cet extrait de code
if __name__ == '__main__': unittest.main()
Implémentez une classe de tests unitaires MyClassTest qui hérite de unittest.TestCase qui teste la fonction carre_parfait du module totest. (En mathématiques, un carré parfait est le carré d'un entier.)
Soit la fonction carre_parfait du module totest ayant pour spécifications :
def carre_parfait (x) : ''' retourne true si l'entier en argument est un carré parfait, false sinon ''' # CODE NON FOURNI
Complétez la classe de test ci-dessous :
import totest import unittest class MyClassTest(unittest.TestCase) : # todo if __name__ == '__main__': unittest.main()
Que donnent-vos tests unitaires pour les implémentations suivantes :
def carre_parfait (x) : return True
def carre_parfait (x) : return False
def carre_parfait (x) : cur = 0 while not cur*cur == x : cur += 1 return True
import math def carre_parfait (x) : root = math.sqrt(x) return root.is_integer()
import math def carre_parfait (x) : if x < 0 : return False root = math.sqrt(x) return root.is_integer()
As the new manager of an Amazon dispatch center, you have to create a class to represent the commands.
A command have the following attributes :
And must have the following methods :
Example : "12, 32 : 5 * 20 = 100"
There must also be class methods :
L'objectif de cette mission est de créer des robots maintenant un état représentant leur position à l'écran et des opérations permettant de les faire bouger sur l'écran. On peut se baser sur plusieurs bibliothèques graphiques pour implémenter de tels robots. On vous fournira une première implémentation basé sur la bibliothèque graphics.py de Python. A vous de proposer une implémentation similaire en utilisant la bibliothèque turtle. Ces deux types de robots sont polymorphe: ils comprennent exactement les mêmes messages, mais l'un est implémenté avec des positions absolues sur un plan XY, tandis que l'autre utilise des positions relatives. Ensuite on vous demandera d'étendre la fonctionnalité de ces robots, ce qui vous permettra de découvrir la puissance du concept de l'héritage pour la réutilisation du code.
Il sera aussi important de bien tester les classes qui vous sont fournies ou que vous implémenterez, avec des tests unitaires. C'est-à-dire que vous créerez, par classe à tester, une classe test correspondante, avec des méthodes tests pour chacune des méthodes de la classe à tester. Ceci vous permet de tester, à une granularité fine, les différents unités (les classes et les méthodes) de votre code orienté objet.
Les différents concepts traités dans cette mission seront, entre autres : le polymorphisme, la composition, la délégation, l'héritage, la duplication et la réutilisation de code, la substitutabilité, l'utilisation de self et de super, et les tests unitaires. Vous travaillerez par groupes de deux.
L'objectif de cette première étape sera de créer une classe test TestXYRobot avec des tests unitaires pour une classe XYRobot qui vous est fournie.
Avant de commencer, vous devez installer le package graphics.py permettant de dessiner des figures simples dans une fenêtre graphique. Cette bibliothèque sera utilisée par la classe XYRobot. Vous pouvez installer ce package facilement depuis Thonny en suivant les étapes suivantes: (1) Sélectionner le menu Tools > Manage Packages... en Thonny; (2) taper graphics.py dans le champs de recherche pour retrouver le bon package et appuyez sur Install .
La classe XYRobot utilise aussi des fonctions mathématiques comme math.cos et math.sin ainsi que des constantes mathématiques comme math.pi, fournies par la bibliothèque math.
La classe XYRobot (dont l'implémentation se trouve dans le fichier XYRobot.py qui vous est fourni sur le site inginious où vous pouvez remettre votre solution) représente un robot ayant un nom, une position (x, y), et une direction (angle). Quand il bouge, ce robot dessine ses mouvements dans une fenêtre représentant un plan XY.
En plus des méthodes __init__ et __str__ et des méthodes accesseurs et mutateurs, cette classe implémente des méthodes comme:
- moveforward(distance) :
fait avancer le robot de distance pixels et trace une ligne lors de ce mouvement
- movebackward(distance) :
fait reculer le robot de distance pixels et trace une ligne lors de ce mouvement
- turnleft() :
fait tourner le robot de 90 degrés vers la gauche (dans le sens contraire des aiguilles d'une montre)
- turnright() :
fait tourner le robot de 90 degrés vers la droite (dans le sens des aiguilles d'une montre)
Appeler ces méthodes fait bouger le robot et dessine des figures sur un plan XY. Il suffit d'exécuter le code fourni dans le fichier pour avoir un exemple concret. N'oubliez pas d'installer d'abord la bibliothèque graphics.py avant d'exécuter ce fichier.
L'implémentation de cette classe XYRobot vous est fournie mais il manque une classe avec des tests unitaires afin de vérifier que le robot bouge et met à jour ses attributs correctement. Implémentez cette classe de test TestXYRobot afin de vérifier le comportement correct des différentes méthodes de la classe XYRobot.
Par exemple, quand on crée un nouveau robot r2d2 = XYRobot("R2-D2") il se trouvera à la position (x=100,y=100) avec comme angle 0 (direction Est). Après avoir exécuté r2d2.moveforward(50) le robot devrait se trouver à la position (x=150, y=100), approximativement, à cause des arrondis dans les calculs. Ensuite, après avoir exécuté r2d2.turnleft() l'angle du robot sera 90° (approximativement), etc.
Soyez le plus complet possible dans vos tests. Mettez votre classe test TestXYRobot dans un fichier séparé TestXYRobot.py qui s'occupera de charger le fichier XYRobot.py et d'exécuter les différents tests sur la classe XYRobot.
Dans cette deuxième étape c'est à vous d'implémenter une classe TurtleBot qui est polymorphe avec la clase XYRobot de l'étape précédente. C'est-à-dire que, en plus des méthodes __init__ et __str__ et des méthodes accesseurs (dont getangle() et position()) et mutateurs, elle doit implémenter les mêmes méthodes :
- moveforward(distance) :
- fait avancer le robot de distance pixels et trace une ligne lors de ce mouvement
- movebackward(distance) :
- fait reculer le robot de distance pixels et trace une ligne lors de ce mouvement
- turnleft() :
- fait tourner le robot de 90 degrés vers la gauche (dans le sens contraire des aiguilles d'une montre)
- turnright() :
- fait tourner le robot de 90 degrés vers la droite (dans le sens des aiguilles d'une montre)
La différence majeure est que la classe TurtleBot fait ces dessins via un objet de type Turtle. Cette classe sera donc assez facile à implémenter en utilisant un objet de type Turtle comme attribut. Le travail d'avancer, tourner et dessiner pourra alors être délégué à ce turtle.
Pour tester son comportement correct, on vous fournit une fichier TestTurtleBot.py (ce fichier se trouve sur le site inginious où vous pouvez remettre votre solution.) Ce fichier test contient des tests unitaires pour votre classe TurtleBot qui doit se trouver dans un fichier TurtleBot.py.
Vous pouvez aussi vérifier que le comportement de votre robot est correct en vérifiant que ce qu'il dessine sur l'écran correspond à ce que vous attendez.
Maintenant, on veut étendre la fonctionnalité des robots. A vos deux classes XYRobot et TurtleBot , ajoutez des méthodes pour:
Plus spécifiquement, il faut ajouter les deux méthodes suivantes:
- getHistory(self) :
retourne l'historique des actions précédentes, comme une liste du style:
[('forward', 50), ('left', 90), ('forward', 50), ...]
- unplay(self) :
- rejouer les actions précédentes dans le sens inverse et vider l'historique après avoir rejoué ces actions
Ajouter aux deux classes de test TestXYRobot et TestTurtleBot les tests unitaires nécessaires pour tester si les deux classes XYRobot et TurtleBot, avec la fonctionnalité historique et la fonctionnalité pour défaire les actions, marchent correctement.
Si vous n'avez pas encore utilisé l'héritage lors de l'étape 3, il est fort probable que vous ayez créé des méthodes très similaires dans les deux classes XYRobot et TurtleBot. Ces deux classes représentent tous les deux un robot ayant une mémoire contenant les actions déjà effectuées ainsi qu'une méthode pour défaire ces actions. Une bonne manière d'éviter le code redondant (recopié/dupliqué dans les deux classes) est de le mettre dans un classe mère Robot commune à ces deux classes, et de déclarer XYRobot et TurtleBot comme des classes filles de cette classe. En particulier, la classe mère peut contenir tout le code pour retenir les actions effectuées et de les refaire dans le sens inverse.
Lors de cet étape on vous demande donc de :
Rejouez les tests unitaires de l'étape 4 sur les nouvelle classes XYRobot et TurtleBot qui héritent de la nouvelle clases mère. Si tout va bien ces tests doivent toujours fonctionner. Si ce n'est pas le cas, adapter le code ou les tests où nécessaire.
Un fichier Robot.py qui contient la classe mère Robot avec le code de gestion de l'historique.
(Si vous n'êtes pas arrivé à l'étape 5, remettez simplement un fichier vide ici.)
Un fichier XYRobot.py qui contient la classe XYRobot qui hérite de Robot.
(Si vous n'êtes pas arrivé à l'étape 5, remettez la version de votre XYRobot sans héritage ici.)
Un fichier TurtleBot.py qui contient la classe TurtleBot qui hérite de Robot.
(Si vous n'êtes pas arrivé à l'étape 5, remettez la version de votre TurtleBot sans héritage ici.)
Un fichier TestXYRobot.py qui contient les tests unitaires pour votre classe XYRobot.
Ce fichier TestXYRobot.py doit charger le fichier XYRobot.py et exécuter les différents tests unitaires sur la classe XYRobot.
Un fichier TestTurtleBot.py qui contient les tests unitaires pour votre classe TurtleBot.
Ce fichier TestTurtleBot.py doit charger le fichier TurtleBot.py et exécuter les différents tests unitaires sur la classe TurtleBot.
Le Service des sports fait appel à vous dans le cadre du développement d'un système informatique pour la gestion des résultats de la course des 24h Vélo de Louvain-la-Neuve. Vous devez réaliser un programme Python permettant d'encoder et de manipuler les différentes données nécessaires (coureurs, résultats, classements) pour tenir à jour les classements en cours de course. Par exemple, il faut pouvoir afficher en temps réel, sur des écrans disposés dans la ville et sur internet, le classement des meilleurs temps pour le tour du circuit.
Pour cette mission, vous allez devoir implémenter une classe Python permettant de manipuler un tel classement et de le mettre à jour avec de nouveaux résultats. Le classement doit être ordonné, et on ne désire pas refaire le tri pour chaque nouveau résultat: il faut donc une structure de liste ordonnée avec des opérations qui maintiennent cet ordre. Vous utiliserez pour cela une liste chaînée.
A l'issue de cette mission, chacun d'entre vous :
Comment bien préparer la mission?
Assister au cours pour comprendre les notions et concepts qui seront abordés.
Lire attentivement et comprendre le chapitre sur Linked lists dans la partie Objects du syllabus théorie :
- 6 - Linked lists
- Embedded references
- The Node class
- Linked lists as collections
- Linked lists and recursion
- Infinite lists
- Ambiguity between lists and nodes
- Modifying lists
- Wrappers and helpers
- The LinkedList class
Essayer et comprendre le code développé dans le syllabus et qui se trouve aussi dans l'annexe correspondant :
- Appendix - Source code of linked lists
- LinkedList class
- Node class
- Creating and using LinkedList objects
Dans cette mission, vous devrez de nouveau utiliser le framework unittest pour produire des classes de test. Relisez la documentation si vous aviez du mal à créer des tests unitaires lors de la mission précédente.
Répondre aux questions à choix multiples et aux autres questions de démarrage.
Ces questions supposent que vous avez lu le chapitre du syllabus théorique sur les listes chaînées et que vous avez lu et testé le code des classes LinkedList et Node dans l'annexe code des classes LinkedList et Node dans l'annexe correspondante.
Les questions à choix multiples de cette mission sont accessibles en ligne depuis https://inginious.info.ucl.ac.be/course/LSINF1101-PYTHON/Session11_QCM.
Répondez aux questions suivantes:
Question 1a : Manipuler une liste vide
Cette question concerne l'utilisation de la classe LinkedList dont le code se trouve dans l'annexe suivante du syllabus : Appendix - Source code of linked lists
Utilisez la classe LinkedList pour créer une liste chaînée vide l.
Donnez une instruction Python pour imprimer le contenu de cette liste l.
Quel résultat sera affiché si on essaie d'imprimer la liste l avec l'instruction print(l)? Expliquez.
Donnez une instruction Python pour imprimer la taille de cette liste l .
Quel résultat sera affiché si on essaie d'imprimer la taille avec l'instruction print(l.__length)? Expliquez.
Question 1b : Remplir la liste
Pour ajouter des éléments à une liste vide, quelqu'un a écrit les instructions suivantes:
l = LinkedList() l.add(3) l.add(2) l.add(1) l.print()
Mais quelqu'un d'autre à écrit:
l = LinkedList() l.add(Node(3)) l.add(Node(2)) l.add(Node(1)) l.print()
Dans les deux cas, exécuter l'instruction l.print() affichera [ 1 2 3 ].
Lequel des deux fragments de code ci-dessus vous semble correct? Pourquoi l'autre imprime quand-même le même résulat?
Indice : qu'est-ce qui se passe si on exécute l'instruction print(Node(Node(Node(1))))? Pouvez-vous expliquer?
Dans le code donné dans l'annexe Appendix - Source code of linked lists la méthode print() de la classe LinkedList imprime les éléments d'une liste chaînée, séparés par une espace. Par exemple:
>>> l = LinkedList() >>> l.add(3) >>> l.add(2) >>> l.add(1) >>> l.print() [ 1 2 3 ]
Or, normalement la convention est de séparer les éléments d'une liste par des virgules. Ajoutez une nouvelle méthode print_avec_virgule() dans la classe LinkedList qui imprime la liste comme suite:
>>> l.print_avec_virgule() [ 1, 2, 3 ]
Attention: il n'y a pas de virgule après le dernier élément ou si on imprime une liste vide.
>>> l = LinkedList() >>> l.print_avec_virgule() [ ]
Généralisez la méthode print_avec_virgule de l'exercice précédente par une méthode print_avec_separateur qui peut prendre n'importe quelle séparateur. Par exemple:
>>> l = LinkedList() >>> l.add(3) >>> l.add(2) >>> l.add(1) >>> l.print_avec_separateur(", ") [ 1, 2, 3 ] >>> l.print_avec_separateur(" ") [ 1 2 3 ] >>> l.print_avec_separateur(" - ") [ 1 - 2 - 3 ]
Changez la méthode d'initialisation __init__ de la classe LinkedList (cf. l'annexe Appendix - Source code of linked lists ), afin qu'elle prenne une liste Python comme paramètre pour initialiser la liste chaînée. Attention: le premier élément de cette liste doit devenir le premier élément de la liste chainée, et ainsi de suite. Spécifier bien cette nouvelle méthode __init__ avec des conditions pre et post.
def __init__(self, lst=[]): self.__length = 0 self.__head = None # initialiser la liste chainêé avec les éléments de lst ...
Par exemple:
>>> l = LinkedList([1,2,3]) >>> l.print() [ 1 2 3 ] >>> l = LinkedList([]) >>> l.print() [ ]
Ajoutez à la classe LinkedList (cf. l'annexe Appendix - Source code of linked lists ) une méthode remove() pour supprimer le noeud en tête de la liste. Si la liste chaînée était déjà vide, cette méthode ne fait rien.
l = LinkedList() l.add(3) l.add(2) l.add(1) l.print() # [ 1 2 3 ] l.remove() l.print() # [ 2 3 ] l.remove() print(l.length) # 1 l.remove() l.print() # [ ] l.remove() l.print() # [ ]
Ajoutez à la classe LinkedList une méthode add_to_end(self, cargo) pour ajouter un noeud en queue de la liste. Ajoutez d'abord un nouvel attribut last dans la méthode d'initialisation pour garder une référence vers ce dernier élément. Changez ensuite la méthode add pour assigner cette référence lors de l'ajout du premier noeud, et la méthode remove pour remettre cette référence à None lorsque la liste est de nouveau vide. Implémentez ensuite la méthode add_to_end.
Est-ce facile d'implémenter une méthode remove_from_end(self, cargo) pour supprimer un élément de la queue d'une LinkedList? Expliquez en mots comment vous feriez.
En Python il est possible d'imbriquer une classe dans une autre. Ceci peut être utile si une classe n'est utilise que par une seule autre classe. C'est le cas ici avec la classe Node. Ce n'est qu'une classe auxiliaire pour la classe LinkedList. Adapter le code de votre classe LinkedList en imbriquant le code de la classe Node dedans. Il suffit d'indenter le code de la classe Node et de changer chaque référence à la classe Node à l'interieur de la classe LinkedList par self.Node.
Quels sont les avantages et désavantages d'imbriquer la classe Node dans la classe LinkedList?
Considérez qu'on désire utiliser la classe LinkedList pour stocker une liste de strings. On souhaite qu'elle soit en permanence en ordre alphabétique croissant.
Dessinez complètement une telle liste chainée contenant les trois strings "abc", "def" et "xyz".
Expliquez en français et dessinez les opérations à effectuer pour ajouter un Node contenant "aaa" dans cette liste.
Même question avec un Node contenant "ghi".
Même question avec un Node contenant "def".
Même question avec un Node contenant "zzz".
Comment feriez-vous pour écrire dans la classe LinkedList cf. l'annexe Appendix - Source code of linked lists ) la méthode insert dont la spécification est la suivante:
def insert(self,s): """ @pre s est un string à insérer dans la liste chainée; self est une liste chaînée dont les noeuds contiennent des strings; les noeuds de la liste sont ordonnées en ordre croissant selon les valeurs (strings) qu'ils contiennent. @post Un noeud contenant le String s a été inséré dans la liste de façon à ce qu'après l'insertion celle-ci soit toujours en ordre croissant. """
Pour rappel, en Python on peut facilement comparer un string avec un autre avec l'opérateur <. Vous pouvez aussi considérez que la classe Node se trouve maintenant à l'intérieur de la classe LinkedList et possède la méthode mutateur suivante:
class Node: def set_next(self,node): self.__next = node
Lors de la mission précédente, les tests unitaires avec unittest ont été introduits. Pour revisiter ce concept, consultez éventuellement un tutoriel online comme https://openclassrooms.com/fr/courses/235344-apprenez-a-programmer-en-python/2235416-les-tests-unitaires-avec-unittest
Créez un test unitaire pour tester la classe LinkedList. Ce test doit contenir des tests pour les différentes méthodes de la classe LinkedList comme size(), first() ou add(valeur) (code original), remove() (question 5), add_to_end(valeur) (question 6) ou insert(valeur) (question 9). Pour vous mettre sur le bon chemin, votre classe test aura la structure suivante:
import unittest class LinkedListTest(unittest.TestCase): """Classe de test utilisé pour tester la classe LinkedList""" def test_size(self): """Test de la methode size() de la classe LinkedList.""" # ... assert*(...) ... def test_first(self): """Test de la methode first() de la classe LinkedList.""" # ... assert*(...) ... def test_add(self): """Test de la methode add(valeur) de la classe LinkedList.""" # ... assert*(...) ... def test_remove(self): """Test de la methode remove() de la classe LinkedList.""" # ... assert*(...) ... def test_add_to_end(self): """Test de la methode add_to_end(valeur) de la classe LinkedList.""" # ... assert*(...) ... def test_insert(self): """Test de la methode insert(valeur) de la classe LinkedList.""" # ... assert*(...) ... if __name__ == '__main__': unittest.main()
Cette mission est un peu différente des autres missions. En effet, vous trouverez sur INGInious une grande partie du code nécessaire, sauf l'implémentation complète de la classe Classement que vous devez produire, ainsi que la classe OrderedLinkedList qu'elle doit utiliser. Comme source d'inspiration on vous donnera également une implémentation assez complète d'une classe LinkedList sur laquelle vous pouvez vous baser, ou que pouvez étendre, pour implémenter la classe OrderedLinkedList. Vous devez également écrire des classes de test détaillées (ClassementTest et OrderedLinkedListTest) en utilisant le framework de test unittest, pour vérifier votre implémentation de la classe Classement.
L'archive contient déjà une implémentation primitive de la classe Classement à base d'un dictionnaire. Néanmoins, cette implémentation est encore incomplète (elle ne gère pas correctement la position des coureurs dans le classement). Vous devez la remplacer par votre implémentation. Elle permet toutefois au programme de fonctionner. Il suffit d'exécuter la méthode de class main() de la classe Main: le programme simulera à la console l'ajout d'un nouveau résultat aléatoire toute seconde.
Lors de la mission, vous allez implémenter un classement de résultats de coureurs, chaque résultat représenté par un coureur (ayant un certain nom et âge) et le temps effectué par ce coureur. Ces résultats doivent être ordonnés dans la liste selon leur temps. Le meilleur résultat (le coureur avec le meilleur temps) se trouve en tête de la liste. Le résultat du coureur le plus lent se trouve en queue de la liste.
Vous allez utiliser une liste chaînée ordonnée (OrderedLinkedList ) pour implémenter un tel classement de coureurs. Réfléchissez aux différentes opérations qu'on peut effectuer sur un classement et comment on devrait les implémenter en utilisant une liste chaînée ordonnée.
Avant de commencez à implémenter cette classe Classement, représentez la structure de votre classement comme liste chaînée ordonnée graphiquement, et montrez ce qu'il se passe lorsque, successivement:
- Vous créez un classement vide;
- Vous ajoutez un résultat pour le coureur A en tête de classement (méthode add);
- Vous ajoutez un résultat pour le coureur B en fin de classement (méthode add);
- Vous ajoutez un résultat pour le coureur C en milieu de classement (méthode add);
- Vous recherchez les résultats des coureurs A, B et C (méthode get);
- Vous recherchez les positions des coureurs A, B et C (méthode getPosition);
- Vous retirez le résultat du coureur B (méthode remove);
- Vous retirez le résultat du coureur A (méthode remove);
- Vous tentez de retirer le résultat d'un coureur D (méthode remove).
Implémentez la classe OrderedLinkedList dont vous aurez besoin pour implémenter votre classe Classement.
Implémentez une classe de test OrderedLinkedListTest pour tester le bon fonctionnement de votre classe OrderedLinkedList.
Sur base de notre implémentation incomplète de la classe Classement au moyen d'une dictionnaire, écrivez un squelette de votre classe Classement au moyen d'une liste chaînée ordonnée, qui remplacera la notre. Respectez bien les pré- et post-conditions des différentes méthodes de la classe :
class Classement : def __init__(self): """ @pre: - @post: un classement vide de taille 0 a été créé """ def size(self): """ Méthode accesseur. Retourne la taille de ce classement. @pre: - @post: Le nombre de résultats actuellement stockés dans ce classement a été retourné. """ def add(self,r): """ Ajoute un résultat r dans ce classement. @pre: r est une instance de la classe Resultat @post: Le résultat r a été inséré selon l'ordre du classement. En cas d'ex-aequo, r est inséré après les autres résultats de même ordre. """ def get(self,c): """ Retourne le résultat d'un coureur donné. @pre c est un Coureur @post retourne le premier (meilleur) Resultat r du coureur c dans le classement. Retourne None si le coureur ne figure pas (encore) dans le classement. """ def get_position(self,c): """ Retourne la meilleure position d'un coureur dans ce classement. @pre c est un Coureur @post retourne un entier représentant la position du coureur c dans ce classement, à partir de 1 pour la tête de ce classement. Si le coureur figure plusieurs fois dans le classement, la première (meilleure) position est retournée. Retourne -1 si le coureur ne figure pas dans le classement. """ def remove(self,c): """ Retire un résultat du classement. @pre c est un Coureur @post retire le premier (meilleur) résultat pour le coureur c du classement. c est comparé au sens de __eq__. Retourne c si un résultat a été retiré, of False si c n'est pas trouvé dans la liste. """ def __str__(self): """ Méthode magique Retourne une représentation string de cet objet. @pre: - @post: Retourne une représentation de ce classement sous forme d'un string, avec une ligne par résultat. """
Ecrivez une classe de test ClassementTest, la plus complète possible permettant de vérifier le bon fonctionnement de votre implémentation de la classe Classement. Utilisez pour cela les méthodes comme assertEqual ou d'autres méthodes définies dans unittest.
Pensez à découper votre classe de test en plusieurs méthodes. Cela facilitera la visualisation des résultats des tests. Vous trouverez sur la tâche INGInious un exemple de classe de test CoureurTest pour la classe Coureur.
Justifiez vos tests dans le fichier README.TXT.
Remplacer votre classe Classement par celle qui vous est fourni et exécutez la méthode main() de la classe Main. Observez que vos classements sont correctement mis à jour. Félicitations!
N'oubliez pas de soumettre votre implémentation des classes OrderedLinkedList et Classement, vos classes de test OrderedLinkedListTest et ClassementTest et votre fichier README.TXT à votre tuteur.
Pour cette mission, vous devez soumettre au serveur de soumissions de programmes du cours, vos classes OrderedLinkedList et Classement dans des fichier orderedlinkedlist.py et classement.py, vos classes test OrderedLinkedListTest et ClassementTest dans des fichiers orderedlinkedlisttest.py et classementtest.py, ainsi que votre fichier README.txt.