diff options
author | Victor Stinner <vstinner@python.org> | 2020-02-07 23:42:51 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-02-07 23:42:51 +0100 |
commit | dc7a50d73a3d16918529669ff7b8783c08cff090 (patch) | |
tree | 03dd057164d756447338063473d5559a1fc9c957 | |
parent | 60ac6ed5579f6666130fc264d3b748ee9575e3aa (diff) | |
download | cpython-git-dc7a50d73a3d16918529669ff7b8783c08cff090.tar.gz |
bpo-39350: Fix fractions for int subclasses (GH-18375)
Fix regression in fractions.Fraction if the numerator and/or the
denominator is an int subclass. The math.gcd() function is now
used to normalize the numerator and denominator. math.gcd() always
return a int type. Previously, the GCD type depended on numerator
and denominator.
-rw-r--r-- | Doc/library/fractions.rst | 4 | ||||
-rw-r--r-- | Lib/fractions.py | 10 | ||||
-rw-r--r-- | Lib/test/test_fractions.py | 22 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2020-02-06-13-34-52.bpo-39350.wRwup1.rst | 5 |
4 files changed, 34 insertions, 7 deletions
diff --git a/Doc/library/fractions.rst b/Doc/library/fractions.rst index d3a42762e3..a4d006eb58 100644 --- a/Doc/library/fractions.rst +++ b/Doc/library/fractions.rst @@ -84,6 +84,10 @@ another rational number, or from a string. The :class:`Fraction` constructor now accepts :class:`float` and :class:`decimal.Decimal` instances. + .. versionchanged:: 3.9 + The :func:`math.gcd` function is now used to normalize the *numerator* + and *denominator*. :func:`math.gcd` always return a :class:`int` type. + Previously, the GCD type depended on *numerator* and *denominator*. .. attribute:: numerator diff --git a/Lib/fractions.py b/Lib/fractions.py index f5a854414c..de3e23b759 100644 --- a/Lib/fractions.py +++ b/Lib/fractions.py @@ -155,13 +155,9 @@ class Fraction(numbers.Rational): if denominator == 0: raise ZeroDivisionError('Fraction(%s, 0)' % numerator) if _normalize: - if type(numerator) is int is type(denominator): - # *very* normal case - g = math.gcd(numerator, denominator) - if denominator < 0: - g = -g - else: - g = _gcd(numerator, denominator) + g = math.gcd(numerator, denominator) + if denominator < 0: + g = -g numerator //= g denominator //= g self._numerator = numerator diff --git a/Lib/test/test_fractions.py b/Lib/test/test_fractions.py index 4649a34bcc..c748533c79 100644 --- a/Lib/test/test_fractions.py +++ b/Lib/test/test_fractions.py @@ -703,6 +703,28 @@ class FractionTest(unittest.TestCase): r = F(13, 7) self.assertRaises(AttributeError, setattr, r, 'a', 10) + def test_int_subclass(self): + class myint(int): + def __mul__(self, other): + return type(self)(int(self) * int(other)) + def __floordiv__(self, other): + return type(self)(int(self) // int(other)) + def __mod__(self, other): + x = type(self)(int(self) % int(other)) + return x + @property + def numerator(self): + return type(self)(int(self)) + @property + def denominator(self): + return type(self)(1) + + f = fractions.Fraction(myint(1 * 3), myint(2 * 3)) + self.assertEqual(f.numerator, 1) + self.assertEqual(f.denominator, 2) + self.assertEqual(type(f.numerator), myint) + self.assertEqual(type(f.denominator), myint) + if __name__ == '__main__': unittest.main() diff --git a/Misc/NEWS.d/next/Library/2020-02-06-13-34-52.bpo-39350.wRwup1.rst b/Misc/NEWS.d/next/Library/2020-02-06-13-34-52.bpo-39350.wRwup1.rst new file mode 100644 index 0000000000..1a09358082 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-02-06-13-34-52.bpo-39350.wRwup1.rst @@ -0,0 +1,5 @@ +Fix regression in :class:`fractions.Fraction` if the numerator and/or the +denominator is an :class:`int` subclass. The :func:`math.gcd` function is now +used to normalize the *numerator* and *denominator*. :func:`math.gcd` always +return a :class:`int` type. Previously, the GCD type depended on *numerator* +and *denominator*. |