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 ;)