Das Modul fractions ermöglicht es uns, rational numbers zu erstellen und mit ihnen zu arbeiten: Brüche mit einem ganzzahligen Zähler, geteilt durch einen ganzzahligen Nenner.
Zum Beispiel können wir 2/3 als exakten Bruch speichern, statt als ungefähren float-Wert 0.6666....
Anders als bei int-, float- und complex-Zahlen haben Brüche keine literale Schreibweise.
Der Konstruktor für Brüche ist jedoch recht flexibel.
Am offensichtlichsten kann er zwei Ganzzahlen entgegennehmen. Gemeinsame Faktoren werden automatisch gekürzt, wodurch der Bruch in seine „einfachste Form“ gebracht wird (die kleinsten ganzen Zahlen, die den Bruch exakt darstellen):
>>> 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
Der Konstruktor für Brüche kann auch eine String-Darstellung einlesen:
>>> f3 = Fraction('2/3')
>>> f3
Fraction(2, 3)
Er kann auch mit float-Parametern arbeiten, aber dabei kann es Probleme mit der ungenauen Art und Weise geben, wie der Dezimalwert intern binär dargestellt wird.
Mehr zu diesem Darstellungsproblem findest du auf der Website 0.30000000000000004 und in Gleitkommaarithmetik: Probleme und Einschränkungen in der Python-Dokumentation.
Für ein zuverlässigeres Ergebnis bei der Verwendung von Gleitkommazahlen mit Brüchen gibt es die Methode <fraction>.limit_denominator().
.limit_denominator() kann einen ganzzahligen Parameter annehmen, wenn du bestimmte Anforderungen hast, aber selbst der Standardwert (max_denominator=1000000) kann gut funktionieren und eine akzeptable, einfache Näherung liefern.
>>> Fraction(1.2)
Fraction(5404319552844595, 4503599627370496)
>>> Fraction(1.2).limit_denominator()
Fraction(6, 5)
Die üblichen arithmetic operators + - * / ** funktionieren mit Brüchen, wie mit anderen numerischen Typen.
Ganzzahlen und andere Fraction-Werte können einbezogen werden und ergeben ein Fraction-Ergebnis.
Wenn du einen float in den Ausdruck einbeziehst, ist das Ergebnis vom Typ float, was zu einem möglichen Genauigkeitsverlust führt.
>>> 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)
Brüche eignen sich hervorragend, um die Genauigkeit während Zwischenberechnungen zu erhalten, sind aber möglicherweise nicht das, was du für die endgültige Ausgabe möchtest.
Es ist möglich, den Zähler und den Nenner einzeln oder als Tupel zu erhalten (tuples werden in einem späteren Konzept behandelt):
>>> Fraction(2, 3).numerator
2
>>> Fraction(2, 3).denominator
3
>>> Fraction(2, 3).as_integer_ratio()
(2, 3)
Verschiedene numerische Standardfunktionen von Python liefern ebenfalls das Ergebnis, das du von der Arbeit mit den Typen int und float erwarten würdest:
>>> 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