O módulo fractions permite-nos criar e trabalhar com rational numbers: frações com um numerador inteiro dividido por um denominador inteiro.
Por exemplo, podemos guardar 2/3 como uma fração exata em vez do valor float aproximado 0.6666...
Ao contrário dos números int, float e complex, as frações não têm uma forma literal.
No entanto, o construtor de frações é bastante flexível.
O caso mais óbvio é que pode receber dois inteiros. Os fatores comuns são removidos automaticamente, convertendo a fração para a sua "forma irredutível" (os menores inteiros que representam a fração com exatidão):
>>> 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
O construtor de frações também consegue interpretar uma representação em string:
>>> f3 = Fraction('2/3')
>>> f3
Fraction(2, 3)
Também pode trabalhar com parâmetros float, mas isso pode dar problemas devido à natureza aproximada de representar o valor decimal internamente em binário.
Para saberes mais sobre este problema de representação, consulta o site 0.30000000000000004 e Aritmética de vírgula flutuante: problemas e limitações na documentação do Python.
Para um resultado mais fiável ao usar números de vírgula flutuante com frações, existe o método <fraction>.limit_denominator().
.limit_denominator() pode receber um parâmetro inteiro se tiveres requisitos específicos, mas até o valor predefinido (max_denominator=1000000) pode funcionar bem e dar uma aproximação aceitável e simples.
>>> Fraction(1.2)
Fraction(5404319552844595, 4503599627370496)
>>> Fraction(1.2).limit_denominator()
Fraction(6, 5)
Os habituais arithmetic operators + - * / ** funcionam com frações, tal como com outros tipos numéricos.
Inteiros e outros Fraction podem ser incluídos e dar um resultado Fraction.
Incluir um float na expressão resulta numa saída float, com uma consequente (possível) perda de precisão.
>>> 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)
As frações são ótimas para preservar a precisão durante cálculos intermédios, mas podem não ser o que queres para o resultado final.
É possível obter o numerador e o denominador individualmente ou como um tuplo (tuples serão abordados num conceito posterior):
>>> Fraction(2, 3).numerator
2
>>> Fraction(2, 3).denominator
3
>>> Fraction(2, 3).as_integer_ratio()
(2, 3)
Várias funções numéricas padrão do Python também dão o resultado que poderias esperar ao trabalhar com os tipos int e 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