Le module fractions permet de créer et de manipuler des rational numbers : des fractions avec un numérateur entier divisé par un dénominateur entier.
Par exemple, on peut stocker 2/3 sous la forme d'une fraction exacte plutôt que la valeur approximative float 0.6666...
Contrairement aux nombres int, float et complex, les fractions n'ont pas de forme littérale.
En revanche, le constructeur de fractions est assez souple.
Le plus évident, c'est qu'il peut prendre deux entiers. Les facteurs communs sont automatiquement supprimés, ce qui convertit la fraction en sa « forme irréductible » (les plus petits entiers qui représentent exactement la fraction) :
>>> from fractions import Fraction
>>> f1 = Fraction(2, 3) # 2/3
>>> f1
Fraction(2, 3)
>>> f2 = Fraction(6, 9)
>>> f2
Fraction(2, 3) # automatically simplified
>>> f1 == f2
True
Le constructeur de fractions peut aussi analyser une représentation sous forme de string :
>>> f3 = Fraction('2/3')
>>> f3
Fraction(2, 3)
Il peut aussi fonctionner avec des paramètres float, mais cela peut poser problème en raison de la nature approximative de la représentation interne d'une valeur décimale en binaire.
Pour en savoir plus sur ce problème de représentation, consulte le site 0.30000000000000004 et la section Arithmétique en virgule flottante : problèmes et limitations de la documentation Python.
Pour un résultat plus fiable lorsqu'on utilise des nombres à virgule flottante avec des fractions, il existe la méthode <fraction>.limit_denominator().
.limit_denominator() peut prendre un paramètre entier si tu as des exigences précises, mais même la valeur par défaut (max_denominator=1000000) peut très bien fonctionner et donner une approximation acceptable et simple.
>>> Fraction(1.2)
Fraction(5404319552844595, 4503599627370496)
>>> Fraction(1.2).limit_denominator()
Fraction(6, 5)
Les arithmetic operators habituels + - * / ** fonctionnent avec les fractions, comme avec les autres types numériques.
Les entiers et les autres Fraction peuvent être inclus et donnent un résultat Fraction.
Inclure un float dans l'expression produit un résultat float, avec une perte de précision (possible) en conséquence.
>>> Fraction(2, 3) + Fraction(1, 4) # addition
Fraction(11, 12)
>>> Fraction(2, 3) * Fraction(6, 5) # multiply fractions
Fraction(4, 5)
>>> Fraction(2, 3) * 6 / 5 # fraction with integers
Fraction(4, 5)
>>> Fraction(2, 3) * 1.2 # fraction with float -> float
0.7999999999999999
>>> Fraction(2, 3) ** 2 # exponentiation with integer
Fraction(4, 9)
Les fractions sont idéales pour préserver la précision pendant les calculs intermédiaires, mais ce n'est pas forcément ce que tu veux pour le résultat final.
Il est possible d'obtenir le numérateur et le dénominateur séparément ou sous forme de tuple (les tuples seront abordés dans un concept ultérieur) :
>>> Fraction(2, 3).numerator
2
>>> Fraction(2, 3).denominator
3
>>> Fraction(2, 3).as_integer_ratio()
(2, 3)
Diverses fonctions numériques standard de Python donnent aussi le résultat que tu attends de int et de float :
>>> round(Fraction(11, 3))
4
>>> from math import floor, ceil
>>> floor(Fraction(11, 3))
3
>>> ceil(Fraction(11, 3))
4
>>> float(Fraction(11, 3))
3.6666666666666665