Skip to content

Exercices: série 1

Classes

Exercice 1

Définir une classe Book avec les propriétés suivantes Python: nb_pages: number,title: string,author: string,isbn: string (ISBN (The International Standard Book Number)) et marked_page: number.

  • Dans la classe Book, définir une méthode mark_page(self, page) qui permet de mettre à jour la valeur de marked_page avec la valeur de l'argument page. ⚠ bien vérifier que page soit < à nb_pages.
  • Créer une classe Library (Bibliothèque) qui contient un tableau de livres qui est passé au constructeur. ⚠ Il y'a une relation entre Library et Book mais ce n'est pas une relation d'héritage. Pour ce cas, on dit que c'est une agrégation.
  • Dans la classe Library, définir une méthode list_authors() qui retourne un tableau contenant uniquement les noms des auteurs.
  • Dans la classe Library, définir une méthode sum_of_marked_pages() qui retourne la somme des marked_page de tous les livres.
  • Instancier une Library avec trois livres
  • Afficher les résultats des appels des méthodes list_authors() et sum_of_marked_pages().
  • Est-ce que la classe Book peuvent exister et être utilisée indépendamment de Library ?

Définir une classe Book avec les propriétés suivantes en readonly: nbPages: number, author: string, isbn: string (ISBN (The International Standard Book Number)) et en protected: markedPage: number.

  • Dans la classe Book, définir une méthode markPage(page: number) qui permet de mettre à jour la valeur de markedPage avec la valeur de l'argument page. ⚠ bien vérifier que page soit < à nbPages.
  • Créer une classe Library (Bibliothèque) qui contient un tableau de livres. ⚠ Il une relation entre Library et Book mais ce n'est pas une relation d'héritage. Pour ce cas, on dit que c'est une agrégation.
  • Dans la classe Library, définir une méthode listAuthors() qui retourne un tableau contenant uniquement les noms des auteurs.
  • Dans la classe Library, définir une méthode sumOfMarkedPages() qui retourne la somme des markedPage de tous les livres.
  • Instancier une Library avec trois livres
  • Afficher les résultats des appels des méthodes listAuthors() et sumOfMarkedPages().
  • Est-ce que la classe Book peuvent exister et être utilisée indépendamment de Library ?
Solution en Python
class Book:
    def __init__(self, nb_pages, title, author, isbn) -> None:
        """ " Constructeur: permet d'initialiser les propriétés"""
        self.nb_pages = nb_pages
        self.title = title
        self.author = author
        self.isbn = isbn
        self.marked_page = 0

    def mark_page(self, page):
        if 0 <= page < self.nb_pages:
            self.marked_page = page
        else:
            print(f"Impossible de marque la page {page}")


class Library:
    def __init__(self, books) -> None:
        self.books = books

    def list_authors_method_1(self):
        authors = []
        for book in self.books:
            authors.append(book.author)
        return authors

    def list_authors_method_2(self):
        """Utilisation de la 'compréhension' de listes"""
        return [book.author for book in self.books]

    def sum_of_marked_pages_method_1(self):
        sum = 0
        for book in self.books:
            sum += book.marked_page
        return sum

    def sum_of_marked_pages_method_2(self):
        return sum([book.marked_page for book in self.books])


book1 = Book(
    190,
    "Matt Weisfeld",
    "Object-Oriented Thought Process, The (Developer's Library)",
    "978-0135181966",
)

book2 = Book(230, "Jon Bodner", "Learning Go", "978-1098139292")

book1.mark_page(-1)
book1.mark_page(800)
book1.mark_page(3)

book2.mark_page(200)

books = [
    book1,
    book2,
    Book(300, "Rob Mastrodomenico", "The Python Book", "978-1119573319"),
]
books[-1].mark_page(33)

library = Library(books)
print("list of authors method 1", library.list_authors_method_1())
print("list of authors method 2", library.list_authors_method_2())
print(
    "sum of marked pages",
    library.sum_of_marked_pages_method_1(),
    library.sum_of_marked_pages_method_2(),
)

Exercice 2

On souhaite représenter des montres et les personnes qui les portent.

Une montre donne l'heure et les minutes. On peut initialiser une montre soit à partir d'un couple heure/minute donné, soit par clonage (en créant une nouvelle montre à partir d'une montre existante). Il doit être possible de faire avancer l'heure d'une montre en ajoutant une minute (attention, les minutes sont limitées à 60 et les heures à 24).

Une personne a un nom et peut éventuellement porter une montre. Une personne peut porter une montre donnée, si elle n'en a pas déjà une. Elle peut aussi enlever sa montre si elle en porte une. Une personne peut demander l'heure à une autre personne, qui lui donne l'heure sous forme d'une chaîne de caractères, en consultant sa montre si elle en a une (sinon elle peut retourner une chaine vide).

  1. Écrivez une classe qui représente les montres telles que décrites ci-dessus.
  2. Créer une montre affichant 13h45 et une autre montre qui est un clone de la première.
  3. Écrivez une classe qui représente les personnes telles que décrites ci-dessus.
  4. On veut faire en sorte que chaque montre ne soit portée que par une seule personne. Proposer des ajouts/modifications des deux classes précédentes pour garantir cela.
  5. Dorénavant, une personne peut porter une montre qui peut être mécanique ou numérique.
Solution en TS
class Watch {
  constructor(private hour: number, private minute: number) {}

  clone(): Watch {
    return new Watch(this.hour, this.minute);
  }

  // Avant d'une minute
  advance() {
    this.minute += 1;
    if (this.minute === 60) {
      this.hour += 1;
      this.minute = 0;
      if (this.hour === 24) {
        this.hour = 0;
      }
    }
  }

  getTimeString(): string {
    return `${this.hour}:${this.minute}`;
  }
}

class Person {
  constructor(readonly name: string, private watch: Watch | null) {}
  // wear:  porter (pour les vêtement)
  wear(watch: Watch) {
    if (this.watch === null) {
      this.watch = watch;
    }
  }
  remove() {
    this.watch = null;
  }
  requestTime(person: Person): string {
    if (person.watch !== null) {
      return `${person.name}: il est ${person.watch.getTimeString()}`;
    }
    // else pas nécessaire car de toute façon, le if fait un return
    return "";
  }
}

const w = new Watch(10, 59);
const w2 = w.clone();
w.advance();
console.log(w, w2);

const tintin = new Person("Tintin", null);
const dupond = new Person("Dupond", w2);

console.log(tintin);
tintin.wear(w);
console.log(tintin);
tintin.remove();
console.log(tintin);

const time = tintin.requestTime(dupond);
console.log(time);
Solution en Python
class Watch:
    def __init__(self, hour, minute, is_mechanical) -> None:
        self.hour = hour
        self.minute = minute
        self.is_mechanical = is_mechanical
        self.is_worn = False

    def __str__(self) -> str:
        return f"Current time {self.hour}:{self.minute}"

    def add(self, minute):
        self.minute += minute
        while self.minute >= 60:
            self.minute -= 60
            self.hour += 1
            if self.hour >= 24:
                self.hour = 0


class Person:
    def __init__(self, name) -> None:
        self.name = name
        self.worn_watch = None

    def wear_watch(self, watch):
        if self.worn_watch == None and not watch.is_worn:
            self.worn_watch = watch
            watch.is_worn = True

    def remove_watch(self):
        if self.worn_watch != None:
            self.worn_watch.is_worn = False
            self.worn_watch = None


antoine = Person("Antoine")
cyril = Person("Cyril")

watch1 = Watch(12, 12, True)
antoine.wear_watch(watch1)

watch2 = Watch(11, 10, True)
cyril.wear_watch(watch1)
print("Montre portée par Cyrtil ?", cyril.worn_watch)

cyril.wear_watch(watch2)
print("Montre portée par Cyrtil ?", cyril.worn_watch)


watch1.add(50)
print("after add 50", watch1)

watch1.add(2000)
print("after add 2000", watch1)

Exercice 3

On veut réaliser un programme de gestion des recettes de cuisine. La classe Ingredient contient ces membres:

  • name, state et unit de type string
  • quantity de type number
  • Le constructeur initialise les propriétés ci-dessus via 4 arguments qui lui seront passés.

L'état d'un ingrédient (son state) peut être: cooked (cuit), raw (cru), whole (entier), ou cut (découpé) ou une combinaison de ces états (par exemple cuit et entier). L'unité peut être une unité de poids (g, kg, etc), de volume (l, ml, cl) ou simplement une cadrinality (cardinalité ou nombre de pièces).

La classe Dish (plat) représente les plats, chaque plat ayant un nom et une liste d'ingrédients. On doit pouvoir créér un plat avec son nom.

  1. Définir les classe Ingredient et Dish
  2. Créer un plat appelé choucroute contenant comme ingrédients : 500g de choucroute cuite, 150g de lard cuit et entier et 2 saucisses entières et cuites
  3. On veut pouvoir comparer les plats et donc leurs ingrédients. Ajoutez une méthode equals dans la classe Ingrédient qui renvoie true si deux ingrédients ont le même nom d'aliment et le même état (pas forcément la même quantité).
    • Si vous faites l'exercice en Python, appeler plutôt cette méthode __eq__. Comparer des plats avec le ==. Que constatez vous ?
  4. Vérifier l'égalité entre quelques ingrédients
  5. Ajoutez une méthode equals dans la classe Dish qui prend en argument un plat, qui renvoie true si deux plats contiennent les mêmes ingrédients, au sens donné juste avant.
    • Si vous faites l'exercice en Python, appeler plutôt cette méthode __eq__. Comparer des plats avec le ==. Que constatez vous ?
  6. Vérifier l'égalité entre quelques plats
Solution en Python
class Ingredient:
    def __init__(self, name, state, unit, quantity):
        self.name = name
        self.state = state
        self.unit = unit
        self.quantity = quantity

    def equals(self, ingredient):
        if self.name == ingredient.name and self.state == ingredient.state:
            return True
        return False

    def __eq__(self, __value):
        return self.equals(__value)


class Dish:
    def __init__(self, name, ingredients):
        self.name = name
        self.ingredients = ingredients

    def __eq__(self, __value) -> bool:
        if len(self.ingredients) != len(__value.ingredients):
            return False
        # On clone les ingrédients pour qu'on puisse éliminer ceux qu'on retouve dans le premier plat
        ingredients_to_compare = __value.ingredients[:]
        for ingredient in self.ingredients:
            for i in range(len(ingredients_to_compare)):
                # Dès qu'on retrouve un ingrédient dans les deux plats on l'enlève
                if ingredients_to_compare[i] == ingredient:
                    del ingredients_to_compare[i]
                    break
        # Si on a vidé la liste, càd que tous les ingédients existents dans les deux plats
        if len(ingredients_to_compare) == 0:
            return True
        return False


ingredient_choucroute1 = Ingredient("Choucroute", "cooked", "g", 500)
ingredient_choucroute2 = Ingredient("Choucroute", "cooked", "kg", 1)
lard = Ingredient("Lard", "cooked", "g", 150)

print(ingredient_choucroute1 == ingredient_choucroute2)
print(ingredient_choucroute1 == lard)

choucroute = Dish(
    "Choucroute",
    [
        ingredient_choucroute1,
        lard,
        Ingredient("Saucisse entière", "cooked", "cadrinality", 2),
    ],
)

choucroute_light = Dish(
    "Choucroute light",
    [ingredient_choucroute1],
)

choucroute_double = Dish(
    "Choucroute",
    [
        ingredient_choucroute2,
        lard,
        Ingredient("Saucisse entière", "cooked", "cadrinality", 4),
    ],
)

print(choucroute_light == choucroute)
print(choucroute == choucroute_double)

Exercice 4

Un Fisherman (pêcheur) a un nom, plusieurs FishingRod (cannes à pêche). Chaque

Modéliser les classes Fisherman

Série2: Héritage et champs statiques

Exercice 2.1

Cet exercice est la suite de l'exo3

  1. On veut faire la distinction entre les ingrédients qu'on peut cuire et ceux qu'on peut découper. Un ingrédient qu'on peut cuire doit avoir une méthode cook() qui le fait passer dans l'état cooked et une température de cuisson. Un ingrédient qu'on peut découper doit avoir une méthode cut() qui le fait passer dans l'état cut.
    • En utilisant uniquement l'héritage, proposer une solution à ce problème
    • (⚠ à ne pas faire en Python) En utilisant utilisant l'héritage et les interfaces, proposer une solution alternative à ce problème
    • (⚠ à ne pas faire en Python) Laquelle des deux solutions vous semble la meilleure ?

Exercice 2.2

Important

Cet exercice est purement théorique. Aucun code n'est demandé.

L'héritage est une relation est. Par exemple, dans un jeu vidéo, si on suppose qu'un Human est Character (personnage) et qu'un Monster (monstre) est aussi un Character, on écrira Human extends Character et Monster extends Character.

  • Donner deux exemples de relation d'héritage.
  • Trouver les relations d'héritage possibles:
    • Être vivant, homme, animal
    • Meuble, fauteuil, Armoire, Table, Jardin
    • Aliment, Pâtisserie, Mille-Feuilles, Croissant, Pain

Exercice 2.3

Essayer de modéliser une classe parente commune (qu'on appelle aussi classe mère) pour ces classes:

  • Ordinateur portable, ordinateur fixe
  • Ordinateur portable, ordinateur fixe, Switch, Xbox, Playstation
  • Voiture, Camion, Vélo, Trottinette

Exercice 2.4

Définir la class StringUtils qui contient les méthodes statiques suivantes:

  • get_first(str): retourne le premier caractère de la chaîne passée en argument.
  • get_last(str): retourne le dernier caractère de la chaîne passée en argument.
  • get_substring(str, first, last): retourne la sous-chaîne comprise entre les index first (inclus) et last (exclu).

Ajoutes les propriétés statiques suivantes:

  • new_line = "\n"
  • tab = "\t"

Utiliser les différentes propriétés et appeler les différentes méthodes.

Solution en Python
class StringUtils:

    new_line = "\n"
    tab = "\t"

    @staticmethod
    def get_first(input):
        return input[0]

    @staticmethod
    def get_last(input):
        return input[-1]

    @staticmethod
    def get_substring(input, first, last):
        return input[first:last]


print(StringUtils.get_substring("python", 2, 4))
print(f"1{StringUtils.tab}2")

Exercice 2.5

Nous souhaitons définir les classes d'un futur jeu MOBA qui va cartonner sévère. Le jeu sera en 2D en vue du dessus. Dans ce jeu nous aurons des héros qui affrontent des monstres.

Les héros et les monstres ont tous des HP (points de vie), des MP (points de magie), un nom, une position dans la carte et une hitbox (rectangle de détection des dégâts).

Un héro peut être soit un tank, soit un mage, soit un soigneur ou soit un guerrier. Chaque héro a un niveau et une compétence propre (le héros fait 10 dégâts, le mage fait 20 dégâts et perd 5 mp, le tank donne 2 de ses hp et le soigneur fait gagner 30 hp et perd 8 mp). Tous les héros commencent au niveau 1 avec un valeur de HP et MP aléatoires compris entre 100 et 200 et entre 50 et 100 respectivement. Les guerriers commencent avec 0 MP.

Un monstre peut être soit un minion, soit un buldozer. Les monstres ont 0 MP et ont des hp aléatoires compris entre 30 et 70.

Nous souhaitons compter le nombre d'instances de chaque héro créé et le nombre d'instances de chaque monstre créé depuis le début du programme.

Définir les différentes classes.

Corrigé

solution uml moba

import random


class Point:
    def __init__(self, x, y) -> None:
        self.x = x
        self.y = y


class Rectangle:
    def __init__(self, p: Point, width: int, height: int) -> None:
        self.p = p
        self.width = width
        self.height = height


class Character:
    def __init__(self, name, hp, mp, position: Point, hitbox: Rectangle) -> None:
        self.name = name
        self.hp = hp
        self.mp = mp
        self.position = position
        self.hitbox = hitbox

    def __str__(self) -> str:
        """ " __nom__ -> dunder (jargon python)"""
        return (
            f"{self.__class__.__name__} (name={self.name}, hp={self.hp}, mp={self.mp})"
        )

    def __repr__(self):
        """print d'une liste appelle cette méthode au lieu de __str__"""
        return self.__str__()


class Hero(Character):
    def __init__(self, name, position, hitbox) -> None:
        hp = random.randint(100, 200)
        mp = random.randint(50, 100)
        super().__init__(name, hp, mp, position, hitbox)
        self.level = 1

    def attack(self, monster):
        monster.hp -= 2
        print(self, "inflige 2 de dégâts. Nouvel état du monstre", monster)

    def perform_skill(self, character):
        print(
            self,
            "a appliqué une compétence a un personnage dont le nouvel état est:",
            character,
        )


class Warrior(Hero):
    def __init__(self, name, position, hitbox) -> None:
        super().__init__(name, position, hitbox)
        self.mp = 0

    def perform_skill(self, character):
        character.hp = max(character.hp - 10, 0)
        super().perform_skill(self, character)


class Mage(Hero):
    def perform_skill(self, character):
        if self.mp < 5:
            return
        character.hp = max(character.hp - 20, 0)
        self.mp -= 5
        super().perform_skill(character)


class Tank(Hero):
    def perform_skill(self, character):
        if self.hp < 2:
            return
        character.hp += 2
        self.hp -= 2
        super().perform_skill(character)


class Healer(Hero):
    def perform_skill(self, character):
        if self.mp < 8:
            return
        character.hp += 30
        self.mp -= 8
        super().perform_skill(character)


class Monster(Character):
    def __init__(self, name, position, hitbox) -> None:
        hp = random.randint(30, 70)
        super().__init__(name, hp, 0, position, hitbox)

    def attack(self, hero):
        hero.hp -= 1
        print(self, "inflige 1 de dégât. Nouvel état du héro", hero)


class Minion(Monster):
    pass


class Buldozer(Monster):
    pass


mage = Mage(
    "Ryze",
    Point(10, 10),
    Rectangle(Point(0, 0), 100, 100),
)

warrior = Warrior(
    "Gerran",
    Point(50, 100),
    Rectangle(Point(0, 0), 100, 100),
)

healer = Healer(
    "Sorakan",
    Point(50, 50),
    Rectangle(Point(0, 0), 100, 100),
)

tanky = Tank(
    "tanky",
    Point(70, 50),
    Rectangle(Point(0, 0), 100, 100),
)

minion1 = Minion(
    "m1",
    Point(200, 500),
    Rectangle(Point(0, 0), 100, 100),
)

minion2 = Minion(
    "m2",
    Point(200, 500),
    Rectangle(Point(0, 0), 100, 100),
)

b1 = Buldozer(
    "b1",
    Point(200, 500),
    Rectangle(Point(0, 0), 100, 100),
)

heroes = [mage, healer, warrior, tanky]
monsters = [minion1, minion2, b1]

print("héros", heroes)
print("monstres", monsters)

warrior.attack(b1)
mage.perform_skill(minion1)
healer.perform_skill(mage)

Exercice 2.6

Une école propose des formations des cours à des étudiants. Les cours sont assurés par des intervenants. Chaque étudiant ou intervenants a un numéro de sécurité sociale qui est de type string, un nom, un prénom. Chaque cours est identifié par son nom et l'enseignant qui l'assure. Chaque étudiant a une liste de cours qu'il suit durant l'année.

Développer un script python qui définit un enseignant en "informatique" et deux enseignants en "maths". Vous pouvez valoriser les autres propriétés à votre guise, tant qu'elles sont valides.

A l'aide de la fonction input, faire en sorte que votre script permette de créer un profil étudiant qui peut choisir deux cours (un cours d'info et un de math ou deux de maths). L'étudiant doit saisir toutes ses informations.

Exercice 2.7

  • On souhaite modéliser une collection de consoles et jeux rétro. Les consoles et les jeux rétro sont des appareil de divertissement les propriétés: name, releaseYear en commun. Les consoles ont en plus la propriété: companyName (la société qui l'a créé). Les jeux vidéos ont comme propriété supplémentaire: l'éditeur du jeu (celui qui le distribue) ainsi que son développeur. On aimerait aussi savoir s'il est indépendant ou pas.
  • Définir les classes nécessaires.
  • Chaque jeu vidéo tient une liste des consoles compatibles (pour les jeux cross-platform) via la propriété: platforms.
  • VideoGameConsole contient en plus la propriété: companyName de type string.
  • Compléter la définition des classes et instancier quelques jeux et consoles.

Sources et références