Aller au contenu

Exercices de POO - série 1

Exercice 1 : Un exemple pas à pas - Bilbo et Gollum⚓︎

Création de la classe

Nous allons commencer par écrire une classe Personnage (qui sera dans un premier temps une coquille vide) et, à partir de cette classe, créer 2 instances : bilbo et gollum.

Tester :

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier

Pour l'instant, notre classe ne sert à rien et nos instances d'objet ne peuvent rien faire. Comme il n'est pas possible de créer une classe totalement vide, nous avons utilisé l'instruction pass qui ne fait rien. Ensuite nous avons créé 2 instances de la classe Personnage : bilbo et gollum.

Le constructeur : la méthode __init__

Les attributs de l'objet doivent être définis dans la classe, à l'aide d'une méthode d'initialisation des attributs.

Une méthode particulière, nommée le constructeur, permet de définir les attributs dès l'instanciation d'un objet.

Cette méthode est définie dans le code source par la ligne : def __init__ (self):

Rappel : La méthode __init__

La méthode __init__ est automatiquement exécutée au moment de la création d'une instance.
Le mot self est obligatoirement le premier argument d'une méthode.
Le mot self représente l'instance. Quand vous définissez une instance de classe (bilbo ou gollum) le nom de votre instance va remplacer le mot self.

Dans le code source, nous allons avoir :

Python
class Personnage_1:
def __init__ (self):
    self.vie = 20
Ensuite lors de la création de l'instance gollum, Python va créer l'attribut vie de la variable gollum.
Cet attribut aura pour valeur de départ la valeur donnée à self.vie dans la méthode __init__

Il se passera exactement la même chose au moment de la création de l'instance bilbo, on aura automatiquement la création de l'attribut vie de la variable bilbo.

Exécutez ce code, puis dans la console, faites afficher les valeurs des attributs vie de gollum et de bilbo

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier

Solution

En console saisir et exécuter ligne par ligne:

Python Console Session
>>> gollum.vie
>>> bilbo.vie
Passer un argument

Imaginons que nos deux personnages n'aient pas au départ les mêmes points de vie ! Pour l'instant, impossible d'introduire cette contrainte (self.vie = 20)

Une méthode, comme une fonction, peut prendre des paramètres.

Le passage de paramètres se fait au moment de la création de l'instance. Modifiez le code pour que le nombre de vies soit un paramètre dont la valeur sera fixée lors de l'instanciation.

Votre code doit :

  • Permettre de passer un argument nb_vies.
  • Créer une instance de Personnage nommée bilbo initialisée avec 20 vies.
  • Créer une autre instance, gollum, avec 15 vies.
  • Afficher les valeurs de l'attribut vie pour bilbo
  • Afficher les valeurs de l'attribut vie pour gollum

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier

Solution
Python
class Personnage_2:
    def __init__(self, nb_vies):
        self.vie = nb_vies

bilbo = Personnage_2(20)
gollum = Personnage_2(15)
print(bilbo.vie)
print(gollum.vie)
Passer deux arguments

Compléter le code pour :

  • Permettre de passer deux paramètres au constructeur.
  • Utiliser les paramètres nb_vies et age pour initialiser les attibuts vie et age.
  • Créer une instance, gollum, avec 20 vies et 127 ans.
  • Afficher ceci (en utilisant les attributs) : gollum a 127 ans et 20 vies

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier

Solution
Python
class Personnage_3:
    def __init__(self, nbre_vies, age):
        self.vie = nbre_vies
        self.age = age

gollum = Personnage_3(20, 127)

print("gollum a ", gollum.age, " ans et ", gollum.vie, " vies")
Ajouter des méthodes

Votre code doit :

  • Ajouter une méthode perd_une_vie qui modifie l'attibut vie (retire une vie)
  • Ajouter une méthode donne_etat qui renvoie la valeur de l'attibut vie
  • Créer un seul personnage, gollum, avec 20 vies et 127 ans.
  • Créer une variable etat_1 égale à son nombre de vie, puis l'afficher
  • Modifier le nombre de vies avec la méthode perd_une_vie
  • Créer une variable etat_2 égale à son nouveau nombre de vie, puis l'afficher.

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier

Solution
Python
class Personnage_4:
    def __init__(self, nbre_vies, age):
        self.vie = nbre_vies
        self.age = age

    def donne_etat(self):
        return self.vie

    def perd_une_vie(self):
        self.vie = self.vie - 1

gollum = Personnage_4(20, 127)
etat_1 = gollum.donne_etat()
print(etat_1)
gollum.perd_une_vie()
etat_2 = gollum.donne_etat()
print(etat_2)
Mauvaise pratique

Vous avez sans doute remarqué que lors de "l'utilisation" des instances bilbo et gollum, nous avons uniquement utilisé des méthodes et nous n'avons plus directement utilisé des attributs (plus de gollum.vie). Il est important de savoir qu'en dehors de la classe l'utilisation des attributs est une mauvaise pratique en programmation orientée objet : les attributs doivent rester "à l'intérieur" de la classe, l'utilisateur de la classe ne doit pas les utiliser directement. Il peut les manipuler, mais uniquement par l'intermédiaire d'une méthode (la méthode perd_une_vie permet de manipuler l'attribut vie)

Boire une potion

Nos personnages peuvent boire une potion qui leur ajoute un point de vie.

Vous devez :

  • Modifier la classe Personnage en ajoutant une méthode boire_potion qui ajoute un point de vie.
  • créer en dehors de la classe une fonction simul(n, a) qui crée un personnage avec n vies, un âge a, lui fait boire une potion, et renvoie le nombre de vies.

Vous pourrez tester par exemple simul(10, 127) dans la console.

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier

Solution
Python
class Personnage_5:

    def __init__(self, nbre_vies, age):
        self.vie = nbre_vies
        self.age = age

    def donne_etat(self):
        return self.vie

    def perd_une_vie(self):
        self.vie = self.vie - 1

    def boire_potion(self):
        self.vie = self.vie + 1

def simul(n, a):
    mon_personnage = Personnage_5(n, a)
    mon_personnage.boire_potion()
    return mon_personnage.donne_etat()
Perdre plusieurs vies

Selon le type d'attaque subie, le personnage peut perdre plus ou moins de points de vie. Pour tenir compte de cet élément, remplacer la méthode perd_une_vie par la méthode perd_vies qui prend le nombre de vies perdues vies_perdues en paramètre.

Vous devez :

  • écrire la méthode perd_vies
  • instancier gollum avec 15 vies et 100 ans
  • lui faire perdre 2 vies avec la méthode perd_vies
  • Faire afficher le nombre de vies avec la méthode donne_etat

Contrainte

vous ne pouvez pas utiliser la notation pointée gollum.vie

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier

Solution
Python
class Personnage_6:
    def __init__(self, nbre_vies, age):
        self.vie = nbre_vies
        self.age = age

    def donne_etat(self):
        return self.vie

    def perd_vies(self, vies_perdues):
        self.vie = self.vie - vies_perdues

    def boire_potion(self):
        self.vie = self.vie + 1

gollum = Personnage_6(15, 100)
gollum.perd_vies(2)
print(gollum.donne_etat())
Un peu de hasard ne nuira pas...

Modifier le code précédent, de façon que perd_vies(self, vies_perdues) retire entre 1 et vies_perdues vies au personnage attaqué.

Vous devez :

  • Modifier la méthode perd_vies
  • Compléter le code de façon à créer un personnage ayant 100 vies, 130 ans, puis qui subit 3 attaques consécutives, lui infligeant chacune 1 à 10 vies perdues... Il faudra ensuite afficher son nombre de vies.

Vous pouvez aussi imaginer vos propres scénarios, en rajoutant le fait de boire de la potion ...

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier

Solution
Python
from random import randint

class Personnage_7:
    def __init__(self, nbre_vies, age):
        self.vie = nbre_vies
        self.age = age

    def donne_etat(self):
        return self.vie

    def perd_vies(self, vies_perdues):
        perdues = randint(1, vies_perdues)
        self.vie = self.vie - perdues

    def boire_potion(self):
        self.vie = self.vie + 1

monstre = Personnage_7(100, 130)
for i in range(3):
    monstre.perd_vies(10)
print(monstre.donne_etat())
Plus mort que mort ?

La méthode perd_vies doit être écrite de façon à ne jamais renvoyer un nombre de vies négatif. Si le nombre de vies est négatif, il doit être mis à 0.

Vous devez :

  • Modifier la méthode perd_vies
  • Completer le code de façon à créer un personnage ayant 100 vies puis qui subit des attaques consécutives, lui infligeant chacune 1 à 10 vies perdues, jusqu'à ce qu'il n'ait plus aucune vie. Le code affichera le nombre d'attaques subies.

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier

Solution
Python
from random import randint

class Personnage_8:
    def __init__(self, nbre_vies, age):
        self.vie = nbre_vies
        self.age = age

    def donne_etat(self):
        return self.vie

    def perd_vies(self, vies_perdues):
        perdues = randint(1, vies_perdues)
        self.vie = self.vie - perdues
        if self.vie < 0:
            self.vie = 0

    def boire_potion(self):
        self.vie = self.vie + 1

monstre = Personnage_8(100, 130)
cpt = 0
while monstre.donne_etat() > 0:
    monstre.perd_vies(10)
    cpt = cpt + 1
print(monstre.donne_etat())
print("Il y a eu ", cpt, " attaques")
Un combat

Organisez un combat virtuel entre nos 2 personnages Bilbo et Gollum:

  • Le code décide aléatoirement quel personnage va attaquer en premier

  • Tant que Bilbo et Gollum sont en vie:

    • Un personnage attaque (la victime perd de 1 à 10 vies)
    • On change de personnage

Tant qu'aucun des deux n'est mort, le combat continue

  • Quand un des deux est mort, on affiche le nombre d'attaques qui ont été menées durant le combat, et l'état de chaque personnage.
Aide pour alterner les combattants

On peut utiliser une variable attaquant = randint(0, 1) Pour changer d'attaquant, on peut écrire attaquant = 1 - attaquant

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier

Solution
Python
from random import randint

class Personnage_9:
    def __init__(self, nbre_vies, age):
        self.vie = nbre_vies
        self.age = age

    def donne_etat(self):
        return self.vie

    def perd_vies(self, vies_perdues):
        perdues = randint(1, vies_perdues)
        self.vie = self.vie - perdues
        if self.vie < 0:
            self.vie = 0

    def boire_potion(self):
        self.vie = self.vie + 1

gollum = Personnage_9(100, 127)
bilbo = Personnage_9(100, 127)
nb_attaques = 0
attaquant = randint(0, 1)
while gollum.donne_etat() > 0 and bilbo.donne_etat() > 0 :
    if attaquant == 0 :
        gollum.perd_vies(10)
    else :
        bilbo.perd_vies(10)
    nb_attaques = nb_attaques + 1
    attaquant = 1 - attaquant
print(nb_attaques)
print("Gollum a ", gollum.donne_etat(), "vies")
print("bilbo a ", bilbo.donne_etat(), "vies")
Facultatif : Faites vos propres règles et implémentez les

Il n'y aura pas de correction toute faite à cette question ... 😊

Modifiez (à votre convenance) les règles et améliorez le programme en modifiant des méthodes ou en implémentant vos propres méthodes.

Quelques propositions si vous ne voyez pas trop quoi ajouter : * Quand une personne attaque, elle peut en réalité décider de ne pas le faire et à la place, boire une potion qui lui rend par exemple 1-8 vies. Le code devra activer cette action si perso.vie est trop bas (à vous de préciser!) * Un personnage peut se mettre en fuite, dans ce cas par exemple, il passe son tour, l'autre peut porter une dernière attaque, mais le combat s'arrête ensuite * Les possibilités sont infinies, ajouter une attaque spéciale, ajouter une vie à chaque tour (autoguérison) etc.

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier

Exercice 2 : exercice "papier"⚓︎

Les comptes

Créez une classe Compte. Un objet de type Compte devra posséder 4 attributs :

  • solde : float : le solde du compte - lors de la création du compte le solde sera 0
  • nom: str: le nom du titulaire du compte
  • prenom: str: le prénom du titulaire du compte
  • num_compte: int : un entier identifiant du compte

Les 3 derniers paramètres devront être définis lors de l'instanciation d'un compte.

Ajoutez des méthodes :

  • une méthode str qui permettra l'affichage du compte avec print(). La méthode doit renvoyer une chaine de caractères comme :

Nom : DURAND
Prénom : Jacques
Solde : 200

👉 pour insérer des retours à la ligne dans une chaîne on ajoute le caractère "\n"

  • une méthode ajouter qui permettra de verser de l'argent sur le compte

  • une méthode debiter qui permettra de retirer de l'argent du compte.
    La méthode débiter doit effectuer le retrait si le solde est suffisant (supérieur au montant qu'on souhaite retirer). Sinon elle doit afficher un message et ne pas effectuer le débit.

  • une méthode get_solde qui renvoie le solde du compte.

Enfin, vous ajouterez dans votre code les instructions pour créer un compte, verser de l'argent puis effectuer un débit.

Solution
Python
class Compte:
    def __init__(self, nom ,prenom, numero )  -> None:
        """
        params : nom: str; prenom: str; numero: int (numéro de compte)
        Le constructeur initialise les attributs, solde est initialisé à 0
        """
        self.solde = 0
        self.nom = nom
        self.prenom = prenom
        self.num_compte = numero


    def __str__(self) -> str :
        """
        param : self : un objet de type compte
        sortie : la fonction renvoie une chaîne de caractère. __str__ est utilisée par print()
        """
        return "Nom : "+self.nom+"\nPrénom : "+self.prenom+"\nSolde : "+str(self.solde)


    def ajouter(self, montant: float) -> None:
        self.solde = self.solde + montant


    def debiter(self, montant:float) -> None:
        if self.solde >= montant :
            self.solde = self.solde - montant
        else:
            print( 'solde insuffisant' )


    def get_solde(self) -> float:
        return self.solde


mon_compte = Compte('Durand', 'Jacques', 13215)
mon_compte.ajouter(200)
mon_compte.debiter(20)
print(mon_compte)
print(mon_compte.solde)

Exercice 3 : Corrigez l'erreur⚓︎

Les voitures

Le code suivant est faux. Vous devez le corriger.

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier

Solution

Dans la méthode nom_puissance les variables nom et couleur ne sont pas définies. Il faut utiliser des attributs.

Python
class Voiture:

    def __init__(self, nom, couleur, puissance):
        self.nom = nom
        self.couleur = couleur
        self.puissance = puissance

    def nom_puissance(self):
        return self.nom + " - puissance : " + str(self.puissance)

voiture_1 = Voiture("Coccinelle", "rouge", 5)
print(voiture_1.nom_puissance())