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éthodemark_page(self, page)
qui permet de mettre à jour la valeur demarked_page
avec la valeur de l'argumentpage
. ⚠ bien vérifier quepage
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 entreLibrary
etBook
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éthodelist_authors()
qui retourne un tableau contenant uniquement les noms des auteurs. - Dans la classe
Library
, définir une méthodesum_of_marked_pages()
qui retourne la somme desmarked_page
de tous les livres. - Instancier une
Library
avec trois livres - Afficher les résultats des appels des méthodes
list_authors()
etsum_of_marked_pages()
. - Est-ce que la classe
Book
peuvent exister et être utilisée indépendamment deLibrary
?
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éthodemarkPage(page: number)
qui permet de mettre à jour la valeur demarkedPage
avec la valeur de l'argumentpage
. ⚠ bien vérifier quepage
soit < ànbPages
. - Créer une classe
Library
(Bibliothèque) qui contient un tableau de livres. ⚠ Il une relation entreLibrary
etBook
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éthodelistAuthors()
qui retourne un tableau contenant uniquement les noms des auteurs. - Dans la classe
Library
, définir une méthodesumOfMarkedPages()
qui retourne la somme desmarkedPage
de tous les livres. - Instancier une
Library
avec trois livres - Afficher les résultats des appels des méthodes
listAuthors()
etsumOfMarkedPages()
. - Est-ce que la classe
Book
peuvent exister et être utilisée indépendamment deLibrary
?
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).
- Écrivez une classe qui représente les montres telles que décrites ci-dessus.
- Créer une montre affichant 13h45 et une autre montre qui est un clone de la première.
- Écrivez une classe qui représente les personnes telles que décrites ci-dessus.
- 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.
- 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
etunit
de typestring
quantity
de typenumber
- 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.
- Définir les classe
Ingredient
etDish
- 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
- 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 ?
- Si vous faites l'exercice en Python, appeler plutôt cette méthode
- Vérifier l'égalité entre quelques ingrédients
- Ajoutez une méthode
equals
dans la classeDish
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 ?
- Si vous faites l'exercice en Python, appeler plutôt cette méthode
- 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
- 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'étatcooked
et une température de cuisson. Un ingrédient qu'on peut découper doit avoir une méthodecut()
qui le fait passer dans l'étatcut
.- 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é
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.