mirror of
https://github.com/python/cpython.git
synced 2025-10-23 07:02:24 +00:00
gh-82017: Support as_integer_ratio() in the Fraction constructor (GH-120271)
Any objects that have the as_integer_ratio() method (e.g. numpy.float128) can now be converted to a fraction.
This commit is contained in:
parent
eaf094c09b
commit
c8d2630995
5 changed files with 66 additions and 13 deletions
|
@ -17,25 +17,30 @@ The :mod:`fractions` module provides support for rational number arithmetic.
|
||||||
A Fraction instance can be constructed from a pair of integers, from
|
A Fraction instance can be constructed from a pair of integers, from
|
||||||
another rational number, or from a string.
|
another rational number, or from a string.
|
||||||
|
|
||||||
|
.. index:: single: as_integer_ratio()
|
||||||
|
|
||||||
.. class:: Fraction(numerator=0, denominator=1)
|
.. class:: Fraction(numerator=0, denominator=1)
|
||||||
Fraction(other_fraction)
|
Fraction(number)
|
||||||
Fraction(float)
|
|
||||||
Fraction(decimal)
|
|
||||||
Fraction(string)
|
Fraction(string)
|
||||||
|
|
||||||
The first version requires that *numerator* and *denominator* are instances
|
The first version requires that *numerator* and *denominator* are instances
|
||||||
of :class:`numbers.Rational` and returns a new :class:`Fraction` instance
|
of :class:`numbers.Rational` and returns a new :class:`Fraction` instance
|
||||||
with value ``numerator/denominator``. If *denominator* is ``0``, it
|
with value ``numerator/denominator``. If *denominator* is ``0``, it
|
||||||
raises a :exc:`ZeroDivisionError`. The second version requires that
|
raises a :exc:`ZeroDivisionError`.
|
||||||
*other_fraction* is an instance of :class:`numbers.Rational` and returns a
|
|
||||||
:class:`Fraction` instance with the same value. The next two versions accept
|
The second version requires that *number* is an instance of
|
||||||
either a :class:`float` or a :class:`decimal.Decimal` instance, and return a
|
:class:`numbers.Rational` or has the :meth:`!as_integer_ratio` method
|
||||||
:class:`Fraction` instance with exactly the same value. Note that due to the
|
(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
|
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
|
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.
|
``Fraction(1.1)`` does *not* return ``Fraction(11, 10)`` as one might expect.
|
||||||
(But see the documentation for the :meth:`limit_denominator` method below.)
|
(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::
|
The usual form for this instance is::
|
||||||
|
|
||||||
[sign] numerator ['/' denominator]
|
[sign] numerator ['/' denominator]
|
||||||
|
@ -110,6 +115,10 @@ another rational number, or from a string.
|
||||||
Formatting of :class:`Fraction` instances without a presentation type
|
Formatting of :class:`Fraction` instances without a presentation type
|
||||||
now supports fill, alignment, sign handling, minimum width and grouping.
|
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
|
.. attribute:: numerator
|
||||||
|
|
||||||
Numerator of the Fraction in lowest term.
|
Numerator of the Fraction in lowest term.
|
||||||
|
|
|
@ -100,6 +100,13 @@ ast
|
||||||
|
|
||||||
(Contributed by Bénédikt Tran in :gh:`121141`.)
|
(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
|
os
|
||||||
--
|
--
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
"""Fraction, infinite-precision, rational numbers."""
|
"""Fraction, infinite-precision, rational numbers."""
|
||||||
|
|
||||||
from decimal import Decimal
|
|
||||||
import functools
|
import functools
|
||||||
import math
|
import math
|
||||||
import numbers
|
import numbers
|
||||||
|
@ -244,7 +243,9 @@ class Fraction(numbers.Rational):
|
||||||
self._denominator = numerator.denominator
|
self._denominator = numerator.denominator
|
||||||
return self
|
return self
|
||||||
|
|
||||||
elif isinstance(numerator, (float, Decimal)):
|
elif (isinstance(numerator, float) or
|
||||||
|
(not isinstance(numerator, type) and
|
||||||
|
hasattr(numerator, 'as_integer_ratio'))):
|
||||||
# Exact conversion
|
# Exact conversion
|
||||||
self._numerator, self._denominator = numerator.as_integer_ratio()
|
self._numerator, self._denominator = numerator.as_integer_ratio()
|
||||||
return self
|
return self
|
||||||
|
@ -278,8 +279,7 @@ class Fraction(numbers.Rational):
|
||||||
numerator = -numerator
|
numerator = -numerator
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise TypeError("argument should be a string "
|
raise TypeError("argument should be a string or a number")
|
||||||
"or a Rational instance")
|
|
||||||
|
|
||||||
elif type(numerator) is int is type(denominator):
|
elif type(numerator) is int is type(denominator):
|
||||||
pass # *very* normal case
|
pass # *very* normal case
|
||||||
|
|
|
@ -354,6 +354,41 @@ class FractionTest(unittest.TestCase):
|
||||||
self.assertRaises(OverflowError, F, Decimal('inf'))
|
self.assertRaises(OverflowError, F, Decimal('inf'))
|
||||||
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):
|
def testFromString(self):
|
||||||
self.assertEqual((5, 1), _components(F("5")))
|
self.assertEqual((5, 1), _components(F("5")))
|
||||||
self.assertEqual((3, 2), _components(F("3/2")))
|
self.assertEqual((3, 2), _components(F("3/2")))
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Added support for converting any objects that have the
|
||||||
|
:meth:`!as_integer_ratio` method to a :class:`~fractions.Fraction`.
|
Loading…
Add table
Add a link
Reference in a new issue