Une utilisation sérieuse
Python s’Objette aux Fractions : Un Voyage dans le Monde des Fractions avec Python
Imaginez que vous travaillez pour une entreprise de développement de logiciels éducatifs. Votre dernière mission est de développer un module pour aider les élèves à comprendre les fractions. Pour cela, vous avez décidé d’utiliser Python et la programmation orientée objet. Votre objectif est de créer une classe
Fractionqui représente une fraction avec un numérateur et un dénominateur. Cette classe doit permettre aux utilisateurs de faire des opérations arithmétiques avec des fractions, comme l’addition, la soustraction, la multiplication et la division, et doit permettre la comparaison des fractions entre-elles. De plus, elle doit garantir que les fractions sont toujours simplifiées à leur forme la plus simple. Alors, prêt à relever le défi ?
N'oubliez pas de tester vos fonctions ! Cet exercice propose une implémentation des fractions en Python, mais vous pouvez ne pas suivre l'énoncé et proposer votre propre version.
Question
Classes et attributs :
Commencez par définir une classe Fraction. Cette classe doit avoir deux attributs : num (numérateur) et denom (dénominateur). Vérifiez que les propriétés nécessaires à nos fractions soient respectées à l'aide de assert.
Indice
Vous pouvez initialiser ces attributs dans la méthode __init__. Assurez-vous que le dénominateur ne soit jamais égal à zéro et que le numérateur et le dénomiateur soient de type int en ajoutant une condition dans la méthode __init__.
Solution
class Fraction:
def __init__(self, num, denom):
assert isinstance(num, int) and isinstance(denom, int), "Les paramètres fournis doivent être des entiers"
assert denom != 0, "Le dénominateur ne peut pas être zéro."
self.num = num
self.denom = denom
Question
Méthodes :
Ajoutez une méthode simplifier à la classe. Cette méthode doit simplifier la fraction en réduisant le numérateur et le dénominateur à leur plus petit commun diviseur.
Ajoutez ensuite une méthode __str__ pour pouvoir afficher convenablement les fractions sur une seule ligne.
Indice
Vous pouvez utiliser l’algorithme d’Euclide pour trouver le plus grand commun diviseur. Consultez l’article Wikipédia sur l’algorithme d’Euclide pour plus de détails.
Solution
def pgcd(a, b):
# L'algorithme d'Euclide pour trouver le plus grand commun diviseur (PGCD) fonctionne comme suit : # 1. Si b est 0, alors le PGCD est a # 2. Sinon, on calcule le PGCD de b et du reste de la division euclidienne de a par b # Cette procédure est répétée jusqu'à ce que b soit égal à zérowhile b != 0:
# On échange les valeurs de a et b, et on remplace b par le reste de la division euclidienne de a par ba, b = b, a % b
# Quand la boucle se termine (c'est-à-dire quand b est égal à zéro), a contient le PGCDreturn a
class Fraction:
# ...def simplifier(self):
# Trouve le PGCD du numérateur et du dénominateurgcd = pgcd(self.num, self.denom)
# Divise le numérateur et le dénominateur par leur PGCDself.num /= gcd
self.denom /= gcd
def __str__(self):
return f"{self.num} / {self.denom}"
Question
Surcharge d’opérateurs :
Vous devez maintenant surcharger les opérateurs +, -, *, /. Ces méthodes doivent retourner une nouvelle instance de Fraction qui est le résultat de l’opération. N’oubliez pas de simplifier la fraction avant de retourner le résultat.
Rajoutez ensuite la surcharge du = et de la comparaison.
Indice
Pour cela, vous devez définir les méthodes spéciales __add__, __sub__, __mul__ ... qui prennent en argument une autre instance de Fraction. Consultez la documentation Python sur les méthodes spéciales pour plus d’informations.
Pour la surcharge du = et de la comparaison, vous devez utiliser les opérateurs présents sur la documentation Python (de __lt__ à __ge__)
Solution
class Fraction:
# ...def __add__(self, other):
# Calcule le numérateur et le dénominateur de la sommenum = self.num * other.denom + other.num * self.denom
denom = self.denom * other.denom
# Crée une nouvelle fraction avec la sommeresult = Fraction(num, denom)
# Simplifie la fraction avant de la retournerresult.simplifier()
return result
# De même pour __sub__, __mul__ et __div__def __eq__(self, other):
# Deux fractions sont égales si leurs numérateurs et dénominateurs sont respectivement égauxreturn self.num == other.num and self.denom == other.denom
# De même pour __ne__def __lt__(self, other):
# Une fraction est inférieure à une autre si le produit de son numérateur et du dénominateur de l'autre est inférieur au produit de son dénominateur et du numérateur de l'autrereturn self.num * other.denom < self.denom * other.num
def __le__(self, other):
# Une fraction est inférieure ou égale à une autre si elle est inférieure ou égale à l'autrereturn self < other or self == other
# De même pour __gt__ et __ge__Question
Encapsulation :
Pour assurer l’encapsulation, vous devez rendre protégés les attributs num et denom. Ensuite, vous devez fournir des méthodes d’accès (getters). Ces méthodes sont généralement appelées get_num, get_denom.
Indice
Pour assurer l’encapsulation, vous devez rendre les attributs protégés en les préfixant par un underscore (_).
Solution
def pgcd(a, b):
# L'algorithme d'Euclide pour trouver le plus grand commun diviseur (PGCD) fonctionne comme suit : # 1. Si b est 0, alors le PGCD est a # 2. Sinon, on calcule le PGCD de b et du reste de la division euclidienne de a par b # Cette procédure est répétée jusqu'à ce que b soit égal à zérowhile b != 0:
# On échange les valeurs de a et b, et on remplace b par le reste de la division euclidienne de a par ba, b = b, a % b
# Quand la boucle se termine (c'est-à-dire quand b est égal à zéro), a contient le PGCDreturn a
class Fraction:
def __init__(self, num, denom):
assert isinstance(num, int) and isinstance(denom, int), "Les paramètres fournis doivent être des entiers"
assert denom != 0, "Le dénominateur ne peut pas être zéro."
self._num = num
self._denom = denom
def simplifier(self):
# Trouve le PGCD du numérateur et du dénominateurgcd = pgcd(self._num, self._denom)
# Divise le numérateur et le dénominateur par leur PGCDself._num /= gcd
self._denom /= gcd
def __str__(self):
return f"{self._num} / {self._denom}"
def __add__(self, other):
# Calcule le numérateur et le dénominateur de la sommenum = self._num * other._denom + other._num * self._denom
denom = self._denom * other._denom
# Crée une nouvelle fraction avec la sommeresult = Fraction(num, denom)
# Simplifie la fraction avant de la retournerresult.simplifier()
return result
# De même pour __sub__, __mul__ et __div__def __eq__(self, other):
# Deux fractions sont égales si leurs numérateurs et dénominateurs sont respectivement égauxreturn self._num == other._num and self._denom == other._denom
# De même pour __ne__def __lt__(self, other):
# Une fraction est inférieure à une autre si le produit de son numérateur et du dénominateur de l'autre est inférieur au produit de son dénominateur et du numérateur de l'autrereturn self._num * other._denom < self._denom * other._num
def __le__(self, other):
# Une fraction est inférieure ou égale à une autre si elle est inférieure ou égale à l'autrereturn self < other or self == other
# De même pour __gt__ et __ge__def get_num(self):
return self._num
def get_denom(self):
return self._denom