POO

Premières classes : exercices

Compte bancaire

Définir une classe CompteBancaire.

Les attributs :

  • le nom du propriétaire
  • le solde

Les méthodes :

  • deposer, méthode permettant de déposer une certaine somme.
  • retirer, méthode permettant le retrait d'une somme donnée.
  • afficher, méthode permettant d'afficher le nom et le solde.
  • Un code possible
  • Le nombre de comptes créés
  • Le nombre de comptes existant

class CompteBancaire:
	
	def __init__(self, nom_proprio, valeur):
		self.nom = nom_proprio
		self.solde = valeur
		self.rouge = False if valeur >= 0 else True
		
	def retirer(self, montant):
		if montant > 0:
			self.solde -= montant
			if self.solde < 0 : self.rouge = True
		
	def deposer(self, montant):
		if montant > 0:
			self.solde += montant
			if self.solde >= 0 : self.rouge = False
			
	def afficher(self):
		ch = "\nCompte de {}.\n".format(self.nom)
		ch += "Solde : {} euros.".format(self.solde)
		if self.rouge:
			ch += "\nAttention, vous êtes dans le rouge."
		print(ch)
		
		
c = CompteBancaire("Marcel", 300)
c.afficher()

c.retirer(500)
c.afficher()

c.deposer(2000)
c.afficher()

Si on veut connaître de façon automatique le nombre d'objets créés, on utilisera un attribut de classe (un attribut qui ne sera donc pas un attribut d'instance, mais un attribut commun à toutes les instances).

Un code possible:


class CompteBancaire:
	
	nombreDeComptes = 0

	def __init__(self, nom_proprio, valeur):
		self.nom = nom_proprio
		self.solde = valeur
		self.rouge = False if valeur >= 0 else True
		CompteBancaire.nombreDeComptes += 1




a = CompteBancaire("Marcellle", 300)
b = CompteBancaire("Gislen", 120)
c = CompteBancaire("Gaspard", 12000)
print(CompteBancaire.nombreDeComptes)

On obtient l'affichage:

3

Avec le code donné précédemment pour dénombrer les comptes créés, un problème se pose si l'on cherche à détruire un compte. Exemple :


class CompteBancaire:
	
	nombreDeComptes = 0

	def __init__(self, nom_proprio, valeur):
		self.nom = nom_proprio
		self.solde = valeur
		self.rouge = False if valeur >= 0 else True
		CompteBancaire.nombreDeComptes += 1

	


a = CompteBancaire("Marcellle", 300)
b = CompteBancaire("Gislen", 120)
c = CompteBancaire("Gaspard", 12000)
print(CompteBancaire.nombreDeComptes)
del(a)
print(CompteBancaire.nombreDeComptes)

On obtient l'affichage:

3
3

Le nombre de comptes est resté le même après destruction de l'objet a.

Pour mettre à jour le nombre de comptes, il faut surcharger le destructeur d'instance:


class CompteBancaire:
	
	nombreDeComptes = 0

	def __init__(self, nom_proprio, valeur):
		self.nom = nom_proprio
		self.solde = valeur
		self.rouge = False if valeur >= 0 else True
		CompteBancaire.nombreDeComptes += 1

	def __del__(self):
		CompteBancaire.nombreDeComptes -= 1
	


a = CompteBancaire("Marcellle", 300)
b = CompteBancaire("Gislen", 120)
c = CompteBancaire("Gaspard", 12000)
print(CompteBancaire.nombreDeComptes)
del(a)
print(CompteBancaire.nombreDeComptes)

On obtient l'affichage:

3
2

Compte bancaire, bis

On reprend la classe CompteBancaire précédente. Ajouter une méthode qui permette de transférer une somme donnée d'un compte à un autre.

  • Un code possible

class CompteBancaire:

	def __init__(self, nom_proprio, valeur):
		self.nom = nom_proprio
		self.solde = valeur
		self.rouge = False if valeur >= 0 else True

	def retirer(self, montant):
		if montant > 0 :
			self.solde -= montant
			if self.solde < 0 : self.rouge = True

	def deposer(self, montant):
		if montant > 0:
			self.solde += montant
			if self.solde >= 0 : self.rouge = False
			
	def transferer(self, montant, autreCompte):
		if montant > 0:
			self.retirer(montant)
			autreCompte.deposer(montant)

	def afficher(self):
		ch = "\nCompte de {}.\n".format(self.nom)
		ch += "Solde : {} euros.".format(self.solde)
		if self.rouge:
			ch += "\nAttention, vous êtes dans le rouge."
		print(ch)


marcel = CompteBancaire("Marcel", 300)
bernadette = CompteBancaire("Bernadette", 1800)

marcel.afficher()
bernadette.afficher()

print("\nTransfert du compte de Bernadette sur celui de Marcel.")

bernadette.transferer(500, marcel)

marcel.afficher()
bernadette.afficher()

Poupées russes

On considère la classe suivante :


class Matrioshka:
	def __init__(self, valeur, poupeerusse):
		self.message = valeur
		self.suivante = poupeerusse

Un objet de type Matrioshka représente une poupée russe sur laquelle est inscrit un message (une valeur numérique ou une chaîne de caractères par exemple).
matrioshka
L'attribut suivante désigne la poupée de hauteur juste inférieure, la première visible à l'intérieur de notre matrioshka lorsqu'on l'ouvre. Cet attribut suivante vaudra None s'il s'agit de la poupée la plus petite.

Lorsqu'on emboîte les poupées l'une dans l'autre, on ne voit donc que le message de la poupée visible qui contient les autres poupées.

Définir une classe Emboitement.

Les attributs :

  • hauteur : nombre de poupées emboîtées (initialisation à 0).
  • sommet : la seule poupée visible, c'est à dire celle qui contient toutes les autres (initialisation à None).

Les méthodes :

  • emboiter(self, valeur) : cette méthode emboite l'emboitement actuel dans une plus grande poupée dont le message sera égal à valeur.
  • deboiter : cette méthode supprime la poupée de plus haut niveau, la poupée de niveau juste en-dessous deviendra donc la poupée visible.
  • valeurVisible : renvoie la valeur inscrite sur la poupée visible.
  • estVide : renvoie True s'il n'y a plus de poupée, False sinon.
  • afficher : une méthode pour afficher la liste des valeurs (valeurs des attributs "message" des poupées emboîtées) dans tout l'emboîtement.
  • Un code possible
  • Remarque


class Matrioshka:
	def __init__(self, valeur, poupeerusse):
		self.message = valeur
		self.suivante = poupeerusse
		
		
class Emboitement:
	
	def __init__(self):
		self.hauteur = 0 # nombre de matrioshkas emboîtées
		self.sommet =  None # matrioska visible
		
	def emboiter(self, valeur):
		s = self.sommet # sommet actuel
		self.sommet = Matrioshka(valeur, s) # définition du nouveau sommet
		self.hauteur += 1
		
	def deboiter(self):
		if not self.estVide():
			s = self.sommet
			self.sommet = s.suivante
			self.hauteur -= 1
		
	def valeurVisible(self):
		if not self.estVide():
			return self.sommet.message
			
	def estVide(self):
		return self.hauteur == 0
		
	def afficher(self):
		print("\nLes messages de l'emboîtement : ")
		poupee = self.sommet
		while poupee != None:
			print("|\t" + str(poupee.message) + "\t|")
			poupee = poupee.suivante
			
			
e = Emboitement()
e.emboiter(3)
e.emboiter(5)
e.emboiter(1)
e.emboiter(12)
e.afficher()


e.deboiter()
print("\nValeur vue après un déboitement :" , e.valeurVisible())
e.deboiter()
print("\nValeur vue après un second déboitement :" , e.valeurVisible())
print()
e.afficher()

L'exercice n'est pas anecdotique : il s'agit essentiellement d'une modélisation de la notion de pile à l'aide d'une liste chaînée.

Undo, redo

On considère le code suivant :


from random import randint


 
class Matrioshka:
	def __init__(self, valeur, poupeerusse):
		self.message = valeur
		self.suivante = poupeerusse
		
		
class Emboitement:
	
	def __init__(self):
		self.hauteur = 0 # nombre de matrioshkas emboîtées
		self.sommet =  None # matrioska visible
		
	def emboiter(self, valeur):
		s = self.sommet # sommet actuel
		self.sommet = Matrioshka(valeur, s) # définition du nouveau sommet
		self.hauteur += 1
		
	def deboiter(self):
		if not self.estVide():
			s = self.sommet
			self.sommet = s.suivante
			self.hauteur -= 1
		return s.message
		
	def valeurVisible(self):
		if not self.estVide():
			return self.sommet.message
			
	def estVide(self):
		return self.hauteur == 0
		
	def Vider(self):
		"""vide la pile"""
		self.sommet =  None
		
	def afficher(self):
		print("\nLes messages de l'emboîtement : ")
		poupee = self.sommet
		while poupee != None:
			print("|\t" + str(poupee.message) + "\t|")
			poupee = poupee.suivante
			
			
			
			
class UndoRedo:
	
	def __init__(self):
		self.pile1 = Emboitement()
		self.pile2 = Emboitement()
		
		
	def do(self):
		self.pile2.Vider()
		self.pile1.emboiter(randint(1,100))
		
	def undo(self):
		self.pile2.emboiter(self.pile1.deboiter())
		
	def redo(self):
		self.pile1.emboiter(self.pile2.deboiter())
		
	def afficher(self):
		print("\nEtat des piles : ")
		p1 = self.pile1.sommet
		p2 = self.pile2.sommet
		while p1 != None or p2 != None:
			if p1 != None:
				print("|\t" + str(p1.message) + "\t|", end="\t")
				p1 = p1.suivante
			if p2 != None:
				print("|\t" + str(p2.message) + "\t|", end='')
				p2 = p2.suivante
			print()
			
		
	
	
a = UndoRedo()

a.do()
a.do()
a.do()
a.do()
a.do()
a.do()
a.do()
a.afficher()

a.undo()
a.undo()
a.undo()
a.afficher()

a.redo()
a.redo()
a.afficher()

a.do()
a.afficher()

Ce code a affiché à la première instruction a.afficher() (ligne 92) :

Etat des piles : 
|	38	|	
|	99	|	
|	53	|	
|	62	|	
|	81	|	
|	84	|	
|	31	|	

Quel est l'affichage obtenu par les autres instructions a.afficher()

  • affichages
  • Remarque

L'affichage complet :

Etat des piles : 
|	38	|	
|	99	|	
|	53	|	
|	62	|	
|	81	|	
|	84	|	
|	31	|	

Etat des piles : 
|	62	|	|	53	|
|	81	|	|	99	|
|	84	|	|	38	|
|	31	|	

Etat des piles : 
|	99	|	|	38	|
|	53	|	
|	62	|	
|	81	|	
|	84	|	
|	31	|	

Etat des piles : 
|	6	|	
|	99	|	
|	53	|	
|	62	|	
|	81	|	
|	84	|	
|	31	|
Cette gestion des deux piles ( ≡ matrioshka) est l'essentiel de ce qui se passe avec les boutons défaire et refaire de certains logiciels. Ou, dans un navigateur, avec la visite de pages web en entrant une url (≡ do) et les boutons "reculer d'une page" (≡ undo) et "avancer d'une page" (≡ redo).