Aller au contenu

TNSI : Exercices POO⚓︎

Encapsulation + Accesseurs / Mutateurs

  1. Créer une classe Python nommée CompteBancaire qui représente un compte bancaire, ayant pour attributs :

    • numeroCompte (type str),
    • nom (nom du propriétaire du compte du type str),
    • solde (type float)

    On pourra utiliser un constructeur __init__() ayant comme paramètres :

    • numeroCompte:str (par défaut numeroCompte="112233"),
    • nom:str (par défaut nom="DUPOND"),
    • solde:float (par défaut solde=0)
  2. Créer une méthode depot(montant:float) qui modélise le dépôt d'un certain montant sur le compte :

    • le solde du compte doit être mis à jour
    • une confirmation de la bonne réception de l'argent sur le compte doit apparaître à l'écran (Terminal)
  3. Créer une méthode retrait(montant:float) qui gère les retraits: mise à jour du solde du compte \(+\) Affichage à l'écran

  4. Créer une méthode agios() permettant d'appliquer les agios à un pourcentage de 5 % du solde du compte
    Pour les heureux qui ne connaîtraient pas : les agios sont des frais d'ajournement que la banque vous déduit de votre compte, lorsque votre solde est en négatif.
  5. Modifier la méthode précédente agios(p:float) de sorte qu'elle applique les agios à hauteur de p% (et non plus forcément 5%)
  6. Créer une méthode infos() permettant d’afficher les infos détaillées du compte (numeroCompte+nom+solde)
  7. Encapsulation
    Rappel : Le concept d'encapsulation permet d’éviter une modification accidentelle (par erreur) ou non souhaitée (piratage) des données/attributs d’un objet en bloquant les droits de lecture et de modification des attributs. Pour rappel :

    • Les attributs privés self.__attributPrive (deux underscores __) sont accessibles UNIQUEMENT depuis l'intérieur de la classe elle-même.
      En particulier, les attributs privés :

      • NE sont PAS accessibles depuis l'extérieur de la classe,
      • NI depuis les classes filles/enfants

      En ce qui concerne les attributs privés, ce comportement est correctement rendu en Python.

    • les attributs protégés self._attributProtege (un seul underscore _) sont accessibles :

      • depuis l'intérieur de la classe elle-même, ou
      • depuis l'une de ses filles/enfants

      En particulier, les attributs protégés NE sont PAS accessibles de puis l'extérieur de la classe. En ce qui concerne les attributs protégés, ce comportement N'est PAS correctement rendu en Python.

    Modifier l'attribut solde en un attribut privé, de sorte que celui-ci ne puisse pas être: ni lu, ni modifié depuis l'extérieur de la classe CompteBancaire. Adapter la classe CompteBancaire pour que celle-ci redevienne fonctionnelle.

  8. Accesseurs 🇫🇷 / Getters 🇬🇧 et Mutateurs 🇫🇷 / Setters 🇬🇧
    Pour pouvoir modifier des attributs privés et/ou protégés, dans certains contextes sécurisés, on peut créer des méthodes qui jouent le rôle d’interface obligatoire:

    • les Accesseurs 🇫🇷 / Getters 🇬🇧 sont des méthodes qui permettent de lire les attributs privés et/ou protégés, dans un contexte où ils ne devraient normalement pas l'être (en contrôlant les informations transmises). Ils sont notés usuellement getXXX() ou get_XXX()XXX désigne un nom à personnaliser selon le contexte.
    • les Mutateurs 🇫🇷 / Setters 🇬🇧 sont des méthodes qui permettent de lire ET/OU de modifier les attributs privés et/ou protégés, dans un contexte où ils ne devraient normalement pas l'être (en contrôlant les informations transmises). Ils sont notés usuellement setXXX() ou set_XXX()XXX désigne un nom à personnaliser selon le contexte.

    Écrire la méthode getSolde() (un getter) qui renvoie la valeur de l'attribut privé __solde : cette méthode est appelable depuis l'extérieur de la classe (testez-la). Écrire la méthode setSolde(valeur) (un setter) qui modifie la valeur de l'attribut privé __solde avec la nouvelle valeur passée en paramètre: cette méthode est appelable depuis l'extérieur de la classe.

Héritage

# Définit une classe 'Fille' (=classe Fille/Enfant)
# qui hérite de la classe 'Mere' (=classe Mère)
class Fille(Mere):
  pass

Dans le code précédent, on dit que la classe Fille/Enfant hérite de la classe Mere.
En pratique, cela impliquera en particulier que:

  • Toute instance de la classe Fille hérite également de (donc accède à) :
    • tous les attributs de la classe Mere
    • toutes les méthodes de la classe Mere
  • Il est possible de surcharger les attributs et/ou les méthodes de la classe Mere, par ceux de la classe Fille/Enfant, Cela veut dire que, on peut redéfinir dans la classe Fille, les attributs et méthodes de la classe Mere (avec EXACTEMENT le même nom), de sorte à pouvoir les remplacer/écraser/modifier dans un contexte plus spécifique :
    • les attributs de la classe Fille seront alors utilisés au lieu de ceux de la classe Mere, sur toutes les instances de la classe Fille :
      ils sont plus spécifiques pour la classe Fille, par rapport à ceux de la classe Mere, donc utilisés prioritairement.
    • les méthodes de la classe Fille seront aussi utilisés au lieu de ceux de la classe Mere, sur toutes les instances de la classe Fille :
      elles sont plus spécifiques pour la classe Fille, par rapport à ceux de la classe Mere, donc utilisés prioritairement.

On considère le code suivant :

class Personnage:
    # contructeur = méthode __init__ avec 2 arguments (sans compter self !) le nombre de vies et le nom du personnage
    def __init__(self, nbreDeVie, nomDuPerso):
        self.vie = nbreDeVie
        self.nom = nomDuPerso
    # voici la méthode qui affiche le nombre de vies du personnage.
    def affichePointVie(self):
        print('Il reste '+str(self.vie)+' points de vie à '+self.nom)
    # voici la méthode qui fait perdre 1 point de vie au personnage qui a subi une attaque
    def perdVie(self):
        print(self.nom+' subit une attaque, il perd une vie')
        self.vie = self.vie-1
# la classe Magicien hérite de la classe Personnage
class Magicien(Personnage):
    # dans def __init__ on retrouve nbreDeVie et nomDuPerso comme dans le def __init__ de la classe Personnage
    def __init__(self, nbreDeVie, nomDuPerso, pointMagie):
        # la ligne suivante est très importante dans le cas d'héritage, il faut systématiquement faire ce genre d'appel :
        # classeparente.__init__(self,arg1,arg2.....) pour hériter de tous les attributs et méthodes de la classe Personnage
        Personnage.__init__(self, nbreDeVie, nomDuPerso)
        # le seul nouvel attribut est self.magie, tous les autres sont hérités de la classe Personnage
        self.magie = pointMagie
    # une méthode uniquement disponible pour les instances de magiciens
    def faireMagie(self):
        print (self.nom+' fait de la magie')
        self.magie = self.magie - 1
        print('Il reste '+str(self.magie)+' points de magie à '+self.nom+'.')
    # les autres méthodes des instances de magicien sont héritées de la classe personnage
# on crée une instance de Magicien
gandalf = Magicien(20,'Gandalf',15)
# applique la méthode affichePointVie à gandalf, cette méthode est héritée de la classe personnage
gandalf.affichePointVie()
# applique la méthode faireMagie à gandalf, cette méthode est uniquement applicable aux instances de la classe magicien
gandalf.faireMagie()
  1. créer une (autre) instance de la classe Magicien, nommée merlin, avec nombreDeVie=30, nomDuPerso="Merlin", et pointMagie=17
  2. afficher les points de vie du magicien Merlin
  3. faire perdre 1 point de vie à Merlin
  4. créer une méthode perdVies(nombre) de la classe Personnage qui:
    • utilise de manière répétée la méthode perdVie() de la même classe Personnage (autant de fois que nécessaire)
    • de sorte que le nombre de vies perdues soit égale à nombre
  5. créer une méthode setPointVie(nombre) (un setter) de la classe Personnage, qui modifie le nombre de vies du personnage, de sorte qu'il soit égal à nombre
  6. créer une méthode creeVie() de la classe Magicien qui ajouter 1 vie au personnage
  7. créer une méthode creeVies(nombre) de la classe Magicien qui:
    • utilise de manière répétée la méthode creeVie() de la même classe Magicien (autant de fois que nécessaire)
    • de sorte que le nombre de vies gagnées soit égale à nombre
  8. créer une nouvelle classe Archer qui hérite de la classe Personnage, de sorte qu'elle dispose :
    • d'un attribut public nom, par défaut "GreenArrow"
    • d'un attribut public nbArcs, par défaut nbArcs=1
    • d'un attribut public nbFleches, par défaut nbFleches=10
      On pourra créer un constructeur __init__() avec ces deux paramètres (et self)
  9. instancier la classe Archer avec une variable greenArrow
  10. Vérifier que l'instance greenArrow de la classe Fille Archer hérite bien de tous les attributs et méthodes de la classe Mère Personnage.
  11. Modifier votre code de sorte à transformer les attributs publics nbArcs et nbFleches, en des attributs privés. créer des méthodes Getters et des Setters pour nbArcs, et pour nbFleches.
  12. Créer une méthode tirerFleche(), qui :
    • met à jour le compteur du nombre de flèches
    • affiche à l'écran que la flèche a été tirée
  13. Créer une méthode magique __repr__() qui formatte l'affichage de l'Archer, lors de l'utilisation d'un print(nom) ou nom
  14. Comment afficher les Points de vie pour greenArrow? (Faites-le) Enlever des points de Vie à greenArrow? (Faites-le)

Classe Chaîne étendue

Coder une classe myString permettant de doter les chaines de caractères des méthodes append() et pop() faisant les mêmes opérations que celles des listes. Exemple si on crée des chaines via l'instanciation s1 = myString("Hello") et s2 = "bonjour", et on lui applique les méthodes :

print(s1.append(" world !")) # affiche  'Hello world !'
print(s2.pop(2))  # affiche 'bonjour'

Classe Temps

On dispose d’un programme permettant de créer un objet de type Temps, qui permet de manipuler un temps entré en heures, minutes et secondes.

Les secondes et les minutes doivent être inférieures à 60. Dans les méthodes ajouterSecondes() et ajouterMinutes(), si les secondes (ou les minutes) dépassent 59, on doit les convertir en minutes (ou respectivement en heure).

La méthode __str__() est une méthode native qui est appelée quand on tente de convertir un objet en chaine de caractères. La méthode de cette classe renvoie une chaine de caractères sous la forme "hh : mm : ss". Par exemple, pour un objet initialisé avec les valeurs 3h,20min et 2s, la méthode renverrait "3 : 20: 2"

La méthode enSecondes() permet de convertir le temps en secondes. La méthode estplusPetit() compare deux objets temps et renvoie True si l'objet temps passé en paramètre est le plus grand des deux.

Compléter ce code aux endroits indiqués par ..., puis ajouter des assertions dans l’initialiseur de Temps. On devra créer deux instances de la classe Temps, avec pour valeurs 1h,59min et 45s et l'autre avec 2h,15min et 13s.

class Temps:
    """Initialise Temps (entre 1 à 4), et Valeur (entre 1 à 13)"""
    def __init__(self, h, m, s):
        self.heures = h
        self.minutes = m
        self.secondes = s

    def ajouterMinutes(self, mins):
        """incrémente les minutes de la valeur mins passée en paramètre"""
        self.minutes = self.minutes + mins
        while ... :
            self.heures += 1
            self.minutes = self.minutes - 60                

    def ajouterSecondes(self, secs):
        """incrémente les secondes de la valeur secondes passée en paramètre"""
        self.secondes = self.secondes + secs
        while ... :
            ...
            self.secondes = self.secondes - 60

    def __str__(self):
        """renvoie une chaine de caractères sous la forme hh : mm : ss"""
        return ...+" : "+...+" : "+...

    def enSecondes(self):
        """Convertie un temps en heures/minutes/secondes en secondes"""
        return ...

    def estplusPetit (self, tps) :
        """Compare deux temps entre eux. Renvoie True si l'objet est plus petit que l'objet tps passé en paramètre"""
        return ...

Débuggage du Code :

#Test du code
...
...
t1.ajouterSecondes(20)

assert str(t1)== "2 : 0 : 5"
assert t1.estplusPetit(t2) == True