Las fracciones aparecen en recetas, proporciones, escalas, música, probabilidades, conversiones y algoritmos matemáticos. Usar float para representar 1/3 o 2/7 produce aproximaciones, mientras Decimal tampoco conserva exactamente los racionales periódicos. El módulo fractions en Python ofrece la clase Fraction, que almacena numerador y denominador enteros y mantiene aritmética racional exacta.
Esta guía explica construcción desde enteros, cadenas, Decimal y floats, reducción automática, limit_denominator(), formateo, validación y la elección entre Fraction, Decimal y float. Complementa nuestros artículos sobre floats en Python, Decimal, enteros, tipos de datos y formato con f-strings.
Qué es un número racional
Un número racional puede escribirse como razón de dos enteros, con denominador distinto de cero. Ejemplos: 1/2, -7/3, 5 y 0.125. La clase Fraction conserva esa estructura:
from fractions import Fraction
mitad = Fraction(1, 2)
tercio = Fraction(1, 3)
print(mitad)
print(tercio)Las instancias son inmutables y hashable, por lo que pueden usarse como claves de diccionario y elementos de conjuntos.
Normalización automática
El constructor reduce la razón a sus términos mínimos y mantiene el denominador positivo.
from fractions import Fraction
print(Fraction(16, -10)) # -8/5
print(Fraction(20, 30)) # 2/3El módulo usa el máximo común divisor. No necesitas simplificar manualmente cada resultado.
Crear Fraction desde cadenas
Las cadenas conservan la intención textual:
from fractions import Fraction
print(Fraction("3/7"))
print(Fraction(" -3 / 7 "))
print(Fraction("0.125"))
print(Fraction("7e-6"))Desde Python 3.12 se permiten espacios alrededor de la barra. Las cadenas decimales finitas se convierten en razones exactas, como 0.125 = 1/8.
Construcción desde float
Un float se convierte a su valor binario exacto, no necesariamente a la fracción decimal esperada.
from fractions import Fraction
print(Fraction(1.1))
# 2476979795053773/2251799813685248La causa se explica en el tutorial oficial de punto flotante: 1.1 no posee representación binaria finita. Usa Fraction("1.1") o Fraction(11, 10) para once décimos exactos.
Fraction desde Decimal
Un Decimal finito se convierte exactamente:
from decimal import Decimal
from fractions import Fraction
valor = Fraction(Decimal("1.1"))
print(valor) # 11/10Es útil cuando la entrada ya fue validada como decimal, pero el algoritmo necesita una razón exacta.
from_number() en Python 3.14
Python 3.14 añade Fraction.from_number(). Acepta enteros, racionales, floats, Decimal y objetos con as_integer_ratio(), pero no cadenas.
from fractions import Fraction
print(Fraction.from_number(5))
print(Fraction.from_number(2.25))La conversión de float sigue conservando el valor binario exacto. La API hace explícito el contrato de entrada numérica.
Aritmética exacta
from fractions import Fraction
a = Fraction(1, 3)
b = Fraction(1, 6)
print(a + b) # 1/2
print(a - b) # 1/6
print(a * b) # 1/18
print(a / b) # 2Los resultados permanecen normalizados. Las potencias con exponentes enteros también funcionan.
numerator y denominator
Los atributos de solo lectura exponen los componentes reducidos:
valor = Fraction(42, 56)
print(valor.numerator) # 3
print(valor.denominator) # 4El denominador siempre es positivo y el signo queda en el numerador.
Recuperar una razón legible desde float
limit_denominator() encuentra la fracción más cercana cuyo denominador no supera un límite.
from fractions import Fraction
aproximacion = Fraction(1.1).limit_denominator()
print(aproximacion) # 11/10La documentación oficial de fractions también muestra la aproximación 355/113 para pi con un denominador máximo de 1000.
pi_aprox = Fraction("3.141592653589793").limit_denominator(1000)
print(pi_aprox) # 355/113Elegir max_denominator
El límite debe reflejar el dominio. Una receta puede necesitar denominadores hasta 16, mientras una aproximación científica puede necesitar miles.
valor = Fraction("0.3332")
print(valor.limit_denominator(10))
print(valor.limit_denominator(1000))Un límite mayor mejora la cercanía, pero puede producir razones difíciles de interpretar. Define un error aceptable y compruébalo.
Comprobar si es entero
Desde Python 3.12, is_integer() indica si el denominador reducido es uno.
print(Fraction(6, 3).is_integer()) # True
print(Fraction(7, 3).is_integer()) # Falseint() trunca hacia cero, por lo que no sirve como prueba de integralidad.
floor, ceil y round
Fraction se integra con las funciones estándar:
import math
from fractions import Fraction
valor = Fraction(7, 3)
print(math.floor(valor))
print(math.ceil(valor))
print(round(valor, 2))round() usa empates al par. Con ndigits, el resultado sigue siendo Fraction.
Formato con f-strings
Las versiones recientes ampliaron el formato. Desde 3.12 se admiten tipos como f, e, g y porcentaje.
valor = Fraction(1, 7)
print(f"{valor:.6f}")
print(f"{valor:.2%}")Python 3.13 añadió alineación, signo, ancho y agrupación sin tipo de presentación.
print(f"{Fraction(103993, 33102):_}")
print(f"{Fraction(3, 1):#}")La opción # fuerza un denominador explícito incluso en enteros.
Comparación y ordenación
Las fracciones se comparan con enteros, floats y Decimal, aunque los floats reflejan aproximación binaria.
valores = [Fraction(2, 3), Fraction(1, 2), Fraction(3, 4)]
print(sorted(valores))Normaliza los tipos cuando importan reglas exactas.
Recetas y proporciones
cantidad_base = Fraction(3, 4)
factor = Fraction(5, 2)
cantidad = cantidad_base * factor
print(cantidad) # 15/8Para mostrar, separa parte entera y resto:
entero = cantidad.numerator // cantidad.denominator
resto = cantidad - entero
print(entero, resto)Probabilidades exactas
Las fracciones funcionan bien en espacios muestrales pequeños:
favorables = 6
posibles = 36
probabilidad = Fraction(favorables, posibles)
print(probabilidad) # 1/6Los modelos grandes pueden crear enteros enormes. Para estadística a escala, float y bibliotecas vectorizadas suelen ser mejores.
Conversiones de unidades
Los factores racionales evitan error acumulado:
pulgadas_por_pie = Fraction(12, 1)
pies = Fraction(5, 2)
pulgadas = pies * pulgadas_por_pie
print(pulgadas) # 30La exactitud no evita mezclar unidades incorrectamente; documenta y valida las unidades.
Crecimiento de numerador y denominador
Las operaciones repetidas pueden crear enteros muy grandes. La normalización elimina factores comunes, pero no limita la magnitud.
valor = Fraction(1, 1)
for n in range(2, 50):
valor += Fraction(1, n)
print(len(str(valor.numerator)))Este crecimiento aumenta CPU y memoria. Los algoritmos aproximados pueden aplicar limit_denominator() en límites controlados o usar otro tipo.
Fraction frente a Decimal
Usa Fraction cuando el valor es naturalmente una razón exacta, como 1/3, escalas o probabilidades combinatorias. Usa Decimal cuando el dominio trabaja con posiciones decimales, dinero y reglas de redondeo.
Decimal representa 0.1 exactamente, pero debe redondear 1/3. Fraction conserva 1/3 exacto, pero reduce 0.10 a 1/10 y no guarda ceros significativos.
Fraction frente a float
Float es mucho más rápido y se integra con NumPy, gráficos y bibliotecas científicas. Fraction está pensado para aritmética racional exacta en volúmenes moderados.
No conviertas a float en medio de un cálculo exacto y esperes recuperar después la razón original. Mantén la conversión en la frontera y documenta tolerancias.
Serialización
JSON no tiene un tipo Fraction nativo. Una representación explícita evita ambigüedad:
registro = {
"numerator": valor.numerator,
"denominator": valor.denominator,
}También puedes enviar "3/4" si el contrato define el formato. No envíes solo una aproximación float cuando debe conservarse exactitud.
Validación de entrada
Captura ValueError para cadenas inválidas y ZeroDivisionError para denominador cero.
def leer_fraccion(texto: str) -> Fraction:
try:
return Fraction(texto)
except (ValueError, ZeroDivisionError) as error:
raise ValueError("Fracción inválida") from errorImpone límites de tamaño a datos no confiables. Enteros con millones de dígitos pueden consumir recursos excesivos.
Pruebas de cálculos racionales
assert Fraction(1, 3) + Fraction(1, 6) == Fraction(1, 2)
assert Fraction("0.125") == Fraction(1, 8)
assert Fraction(1.1).limit_denominator() == Fraction(11, 10)Prueba signos, denominador cero, parsing, formato y límites de aproximación.
Errores frecuentes
- Usar
Fraction(1.1)esperando 11/10. - Permitir denominadores ilimitados desde entrada no confiable.
- Usar Fraction para dinero y perder la escala decimal.
- Convertir a float demasiado pronto.
- Interpretar
int(fraccion)como redondeo. - Elegir un límite sin medir el error.
- Serializar solo una aproximación decimal.
- Ignorar el crecimiento de enteros.
Buenas prácticas
- Construye valores exactos desde enteros o cadenas.
- Prefiere Decimal cuando la escala decimal forma parte del contrato.
- Define límites según el dominio.
- Mantén Fraction en el núcleo exacto.
- Valida entrada y tamaño.
- Serializa numerador y denominador.
- Mide rendimiento en bucles grandes.
- Prueba aproximaciones y negativos.
Conclusión
El módulo fractions en Python ofrece aritmética racional exacta integrada con el modelo numérico de la lengua. Fraction normaliza automáticamente, acepta varias entradas, ofrece aproximaciones controladas y admite formato moderno.
El tipo adecuado depende del dominio. Fraction conserva razones como 1/3, Decimal conserva valores decimales y reglas monetarias, y float prioriza rendimiento e integración numérica. Elegir el tipo correcto evita aproximaciones innecesarias y produce resultados más claros, comprobables y fáciles de explicar.







