A Fraction instance can be constructed from a pair of integers, from
another rational number, or from a string.
+.. index:: single: as_integer_ratio()
+
.. class:: Fraction(numerator=0, denominator=1)
- Fraction(other_fraction)
- Fraction(float)
- Fraction(decimal)
+ Fraction(number)
Fraction(string)
The first version requires that *numerator* and *denominator* are instances
of :class:`numbers.Rational` and returns a new :class:`Fraction` instance
with value ``numerator/denominator``. If *denominator* is ``0``, it
- raises a :exc:`ZeroDivisionError`. The second version requires that
- *other_fraction* is an instance of :class:`numbers.Rational` and returns a
- :class:`Fraction` instance with the same value. The next two versions accept
- either a :class:`float` or a :class:`decimal.Decimal` instance, and return a
- :class:`Fraction` instance with exactly the same value. Note that due to the
+ raises a :exc:`ZeroDivisionError`.
+
+ The second version requires that *number* is an instance of
+ :class:`numbers.Rational` or has the :meth:`!as_integer_ratio` method
+ (this includes :class:`float` and :class:`decimal.Decimal`).
+ It returns a :class:`Fraction` instance with exactly the same value.
+ Assumed, that the :meth:`!as_integer_ratio` method returns a pair
+ of coprime integers and last one is positive.
+ Note that due to the
usual issues with binary floating-point (see :ref:`tut-fp-issues`), the
argument to ``Fraction(1.1)`` is not exactly equal to 11/10, and so
``Fraction(1.1)`` does *not* return ``Fraction(11, 10)`` as one might expect.
(But see the documentation for the :meth:`limit_denominator` method below.)
- The last version of the constructor expects a string or unicode instance.
+
+ The last version of the constructor expects a string.
The usual form for this instance is::
[sign] numerator ['/' denominator]
Formatting of :class:`Fraction` instances without a presentation type
now supports fill, alignment, sign handling, minimum width and grouping.
+ .. versionchanged:: 3.14
+ The :class:`Fraction` constructor now accepts any objects with the
+ :meth:`!as_integer_ratio` method.
+
.. attribute:: numerator
Numerator of the Fraction in lowest term.
(Contributed by Bénédikt Tran in :gh:`121141`.)
+fractions
+---------
+
+Added support for converting any objects that have the
+:meth:`!as_integer_ratio` method to a :class:`~fractions.Fraction`.
+(Contributed by Serhiy Storchaka in :gh:`82017`.)
+
os
--
"""Fraction, infinite-precision, rational numbers."""
-from decimal import Decimal
import functools
import math
import numbers
self._denominator = numerator.denominator
return self
- elif isinstance(numerator, (float, Decimal)):
+ elif (isinstance(numerator, float) or
+ (not isinstance(numerator, type) and
+ hasattr(numerator, 'as_integer_ratio'))):
# Exact conversion
self._numerator, self._denominator = numerator.as_integer_ratio()
return self
numerator = -numerator
else:
- raise TypeError("argument should be a string "
- "or a Rational instance")
+ raise TypeError("argument should be a string or a number")
elif type(numerator) is int is type(denominator):
pass # *very* normal case
self.assertRaises(OverflowError, F, Decimal('inf'))
self.assertRaises(OverflowError, F, Decimal('-inf'))
+ def testInitFromIntegerRatio(self):
+ class Ratio:
+ def __init__(self, ratio):
+ self._ratio = ratio
+ def as_integer_ratio(self):
+ return self._ratio
+
+ self.assertEqual((7, 3), _components(F(Ratio((7, 3)))))
+ errmsg = "argument should be a string or a number"
+ # the type also has an "as_integer_ratio" attribute.
+ self.assertRaisesRegex(TypeError, errmsg, F, Ratio)
+ # bad ratio
+ self.assertRaises(TypeError, F, Ratio(7))
+ self.assertRaises(ValueError, F, Ratio((7,)))
+ self.assertRaises(ValueError, F, Ratio((7, 3, 1)))
+ # only single-argument form
+ self.assertRaises(TypeError, F, Ratio((3, 7)), 11)
+ self.assertRaises(TypeError, F, 2, Ratio((-10, 9)))
+
+ # as_integer_ratio not defined in a class
+ class A:
+ pass
+ a = A()
+ a.as_integer_ratio = lambda: (9, 5)
+ self.assertEqual((9, 5), _components(F(a)))
+
+ # as_integer_ratio defined in a metaclass
+ class M(type):
+ def as_integer_ratio(self):
+ return (11, 9)
+ class B(metaclass=M):
+ pass
+ self.assertRaisesRegex(TypeError, errmsg, F, B)
+ self.assertRaisesRegex(TypeError, errmsg, F, B())
+
def testFromString(self):
self.assertEqual((5, 1), _components(F("5")))
self.assertEqual((3, 2), _components(F("3/2")))
--- /dev/null
+Added support for converting any objects that have the
+:meth:`!as_integer_ratio` method to a :class:`~fractions.Fraction`.