From 9cc62f7c849f4e0d60efb71f8eec6bd4933ec1d7 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Fri, 10 Feb 2023 09:51:07 +0300 Subject: [PATCH 01/11] gh-101773: Optimize creation of Fraction's in private methods --- Lib/fractions.py | 70 +++++++++++-------- Lib/test/test_fractions.py | 1 + Lib/test/test_numeric_tower.py | 2 +- ...-02-10-11-59-13.gh-issue-101773.J_kI7y.rst | 1 + 4 files changed, 42 insertions(+), 32 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-02-10-11-59-13.gh-issue-101773.J_kI7y.rst diff --git a/Lib/fractions.py b/Lib/fractions.py index 49a3f2841a2ed4..174b1e0215619e 100644 --- a/Lib/fractions.py +++ b/Lib/fractions.py @@ -183,7 +183,7 @@ class Fraction(numbers.Rational): __slots__ = ('_numerator', '_denominator') # We're immutable, so use __new__ not __init__ - def __new__(cls, numerator=0, denominator=None, *, _normalize=True): + def __new__(cls, numerator=0, denominator=None): """Constructs a Rational. Takes a string like '3/2' or '1.5', another Rational instance, a @@ -279,12 +279,11 @@ def __new__(cls, numerator=0, denominator=None, *, _normalize=True): if denominator == 0: raise ZeroDivisionError('Fraction(%s, 0)' % numerator) - if _normalize: - g = math.gcd(numerator, denominator) - if denominator < 0: - g = -g - numerator //= g - denominator //= g + g = math.gcd(numerator, denominator) + if denominator < 0: + g = -g + numerator //= g + denominator //= g self._numerator = numerator self._denominator = denominator return self @@ -315,6 +314,13 @@ def from_decimal(cls, dec): (cls.__name__, dec, type(dec).__name__)) return cls(*dec.as_integer_ratio()) + @classmethod + def _from_pair(cls, num, den): + obj = super(Fraction, cls).__new__(cls) + obj._numerator = num + obj._denominator = den + return obj + def is_integer(self): """Return True if the Fraction is an integer.""" return self._denominator == 1 @@ -380,9 +386,9 @@ def limit_denominator(self, max_denominator=1000000): # the distance from p1/q1 to self is d/(q1*self._denominator). So we # need to compare 2*(q0+k*q1) with self._denominator/d. if 2*d*(q0+k*q1) <= self._denominator: - return Fraction(p1, q1, _normalize=False) + return Fraction._from_pair(p1, q1) else: - return Fraction(p0+k*p1, q0+k*q1, _normalize=False) + return Fraction._from_pair(p0+k*p1, q0+k*q1) @property def numerator(a): @@ -703,13 +709,13 @@ def _add(a, b): nb, db = b._numerator, b._denominator g = math.gcd(da, db) if g == 1: - return Fraction(na * db + da * nb, da * db, _normalize=False) + return Fraction._from_pair(na * db + da * nb, da * db) s = da // g t = na * (db // g) + nb * s g2 = math.gcd(t, g) if g2 == 1: - return Fraction(t, s * db, _normalize=False) - return Fraction(t // g2, s * (db // g2), _normalize=False) + return Fraction._from_pair(t, s * db) + return Fraction._from_pair(t // g2, s * (db // g2)) __add__, __radd__ = _operator_fallbacks(_add, operator.add) @@ -719,13 +725,13 @@ def _sub(a, b): nb, db = b._numerator, b._denominator g = math.gcd(da, db) if g == 1: - return Fraction(na * db - da * nb, da * db, _normalize=False) + return Fraction._from_pair(na * db - da * nb, da * db) s = da // g t = na * (db // g) - nb * s g2 = math.gcd(t, g) if g2 == 1: - return Fraction(t, s * db, _normalize=False) - return Fraction(t // g2, s * (db // g2), _normalize=False) + return Fraction._from_pair(t, s * db) + return Fraction._from_pair(t // g2, s * (db // g2)) __sub__, __rsub__ = _operator_fallbacks(_sub, operator.sub) @@ -741,15 +747,17 @@ def _mul(a, b): if g2 > 1: nb //= g2 da //= g2 - return Fraction(na * nb, db * da, _normalize=False) + return Fraction._from_pair(na * nb, db * da) __mul__, __rmul__ = _operator_fallbacks(_mul, operator.mul) def _div(a, b): """a / b""" # Same as _mul(), with inversed b. - na, da = a._numerator, a._denominator nb, db = b._numerator, b._denominator + if nb == 0: + raise ZeroDivisionError('Fraction(%s, 0)' % db) + na, da = a._numerator, a._denominator g1 = math.gcd(na, nb) if g1 > 1: na //= g1 @@ -761,7 +769,7 @@ def _div(a, b): n, d = na * db, nb * da if d < 0: n, d = -n, -d - return Fraction(n, d, _normalize=False) + return Fraction._from_pair(n, d) __truediv__, __rtruediv__ = _operator_fallbacks(_div, operator.truediv) @@ -798,17 +806,17 @@ def __pow__(a, b): if b.denominator == 1: power = b.numerator if power >= 0: - return Fraction(a._numerator ** power, - a._denominator ** power, - _normalize=False) - elif a._numerator >= 0: - return Fraction(a._denominator ** -power, - a._numerator ** -power, - _normalize=False) + return Fraction._from_pair(a._numerator ** power, + a._denominator ** power) + elif a._numerator > 0: + return Fraction._from_pair(a._denominator ** -power, + a._numerator ** -power) + elif a._numerator == 0: + raise ZeroDivisionError('Fraction(%s, 0)' % + a._denominator ** -power) else: - return Fraction((-a._denominator) ** -power, - (-a._numerator) ** -power, - _normalize=False) + return Fraction._from_pair((-a._denominator) ** -power, + (-a._numerator) ** -power) else: # A fractional power will generally produce an # irrational number. @@ -832,15 +840,15 @@ def __rpow__(b, a): def __pos__(a): """+a: Coerces a subclass instance to Fraction""" - return Fraction(a._numerator, a._denominator, _normalize=False) + return Fraction._from_pair(a._numerator, a._denominator) def __neg__(a): """-a""" - return Fraction(-a._numerator, a._denominator, _normalize=False) + return Fraction._from_pair(-a._numerator, a._denominator) def __abs__(a): """abs(a)""" - return Fraction(abs(a._numerator), a._denominator, _normalize=False) + return Fraction._from_pair(abs(a._numerator), a._denominator) def __int__(a, _index=operator.index): """int(a)""" diff --git a/Lib/test/test_fractions.py b/Lib/test/test_fractions.py index 3bc6b409e05dc3..e112f49d2e7944 100644 --- a/Lib/test/test_fractions.py +++ b/Lib/test/test_fractions.py @@ -488,6 +488,7 @@ def testArithmetic(self): self.assertEqual(F(5, 6), F(2, 3) * F(5, 4)) self.assertEqual(F(1, 4), F(1, 10) / F(2, 5)) self.assertEqual(F(-15, 8), F(3, 4) / F(-2, 5)) + self.assertRaises(ZeroDivisionError, operator.truediv, F(1), F(0)) self.assertTypedEquals(2, F(9, 10) // F(2, 5)) self.assertTypedEquals(10**23, F(10**23, 1) // F(1)) self.assertEqual(F(5, 6), F(7, 3) % F(3, 2)) diff --git a/Lib/test/test_numeric_tower.py b/Lib/test/test_numeric_tower.py index 9cd85e13634c2b..af9b3ab7f47d66 100644 --- a/Lib/test/test_numeric_tower.py +++ b/Lib/test/test_numeric_tower.py @@ -145,7 +145,7 @@ def test_fractions(self): # The numbers ABC doesn't enforce that the "true" division # of integers produces a float. This tests that the # Rational.__float__() method has required type conversions. - x = F(DummyIntegral(1), DummyIntegral(2), _normalize=False) + x = F._from_pair(DummyIntegral(1), DummyIntegral(2)) self.assertRaises(TypeError, lambda: x.numerator/x.denominator) self.assertEqual(float(x), 0.5) diff --git a/Misc/NEWS.d/next/Library/2023-02-10-11-59-13.gh-issue-101773.J_kI7y.rst b/Misc/NEWS.d/next/Library/2023-02-10-11-59-13.gh-issue-101773.J_kI7y.rst new file mode 100644 index 00000000000000..1741b4a10ba24b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-02-10-11-59-13.gh-issue-101773.J_kI7y.rst @@ -0,0 +1 @@ +Optimize :class:`fractions.Fraction` for small components. From 3625d34155b4d6d8665c665a85ccd701af47d336 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sat, 11 Feb 2023 14:04:55 +0300 Subject: [PATCH 02/11] Adjust signature of _from_pair() and add docstring --- Lib/fractions.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Lib/fractions.py b/Lib/fractions.py index 174b1e0215619e..6afeb2cc16edc1 100644 --- a/Lib/fractions.py +++ b/Lib/fractions.py @@ -315,10 +315,15 @@ def from_decimal(cls, dec): return cls(*dec.as_integer_ratio()) @classmethod - def _from_pair(cls, num, den): + def _from_pair(cls, numerator, denominator, /): + """Convert a pair of int's to a rational number. + + The ratio of integers should be in lowest terms and + the denominator is positive. + """ obj = super(Fraction, cls).__new__(cls) - obj._numerator = num - obj._denominator = den + obj._numerator = numerator + obj._denominator = denominator return obj def is_integer(self): From 9e66b5a1c11ba432518acc23b17a7cf447870989 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sat, 11 Feb 2023 14:09:08 +0300 Subject: [PATCH 03/11] Use new helper in from_float/Decimal --- Lib/fractions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/fractions.py b/Lib/fractions.py index 6afeb2cc16edc1..ffbd16f601304d 100644 --- a/Lib/fractions.py +++ b/Lib/fractions.py @@ -300,7 +300,7 @@ def from_float(cls, f): elif not isinstance(f, float): raise TypeError("%s.from_float() only takes floats, not %r (%s)" % (cls.__name__, f, type(f).__name__)) - return cls(*f.as_integer_ratio()) + return cls._from_pair(*f.as_integer_ratio()) @classmethod def from_decimal(cls, dec): @@ -312,7 +312,7 @@ def from_decimal(cls, dec): raise TypeError( "%s.from_decimal() only takes Decimals, not %r (%s)" % (cls.__name__, dec, type(dec).__name__)) - return cls(*dec.as_integer_ratio()) + return cls._from_pair(*dec.as_integer_ratio()) @classmethod def _from_pair(cls, numerator, denominator, /): From 43243910dc37b3dbdcf2bfe776eb0689028565be Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sat, 11 Feb 2023 15:06:17 +0300 Subject: [PATCH 04/11] Restore back _normalize kwarg for the Fraction.__new__() --- Lib/fractions.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Lib/fractions.py b/Lib/fractions.py index ffbd16f601304d..6a0f8a038b71ca 100644 --- a/Lib/fractions.py +++ b/Lib/fractions.py @@ -183,7 +183,7 @@ class Fraction(numbers.Rational): __slots__ = ('_numerator', '_denominator') # We're immutable, so use __new__ not __init__ - def __new__(cls, numerator=0, denominator=None): + def __new__(cls, numerator=0, denominator=None, *, _normalize=True): """Constructs a Rational. Takes a string like '3/2' or '1.5', another Rational instance, a @@ -279,11 +279,12 @@ def __new__(cls, numerator=0, denominator=None): if denominator == 0: raise ZeroDivisionError('Fraction(%s, 0)' % numerator) - g = math.gcd(numerator, denominator) - if denominator < 0: - g = -g - numerator //= g - denominator //= g + if _normalize: + g = math.gcd(numerator, denominator) + if denominator < 0: + g = -g + numerator //= g + denominator //= g self._numerator = numerator self._denominator = denominator return self From b0dca38aa3e0af061127bac6a1225cb05eaadfa4 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sat, 11 Feb 2023 15:23:42 +0300 Subject: [PATCH 05/11] Amend 3625d34155 --- Lib/fractions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/fractions.py b/Lib/fractions.py index 6a0f8a038b71ca..48429a8e838634 100644 --- a/Lib/fractions.py +++ b/Lib/fractions.py @@ -317,7 +317,7 @@ def from_decimal(cls, dec): @classmethod def _from_pair(cls, numerator, denominator, /): - """Convert a pair of int's to a rational number. + """Convert a pair of int's to a rational number, for internal use. The ratio of integers should be in lowest terms and the denominator is positive. From 3a8506dc2b5962600fbe140956b03c495910e41c Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Mon, 27 Feb 2023 06:14:20 +0300 Subject: [PATCH 06/11] Nitpick on docstring (int's -> ints) --- Lib/fractions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/fractions.py b/Lib/fractions.py index 48429a8e838634..4a99087f0099cb 100644 --- a/Lib/fractions.py +++ b/Lib/fractions.py @@ -317,7 +317,7 @@ def from_decimal(cls, dec): @classmethod def _from_pair(cls, numerator, denominator, /): - """Convert a pair of int's to a rational number, for internal use. + """Convert a pair of ints to a rational number, for internal use. The ratio of integers should be in lowest terms and the denominator is positive. From 13262af7b792160a5ffec326c7be140afe1dbb40 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Mon, 27 Feb 2023 06:18:21 +0300 Subject: [PATCH 07/11] Revert "Restore back _normalize kwarg for the Fraction.__new__()" This reverts commit 43243910dc37b3dbdcf2bfe776eb0689028565be. --- Lib/fractions.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Lib/fractions.py b/Lib/fractions.py index 4a99087f0099cb..3d332194521227 100644 --- a/Lib/fractions.py +++ b/Lib/fractions.py @@ -183,7 +183,7 @@ class Fraction(numbers.Rational): __slots__ = ('_numerator', '_denominator') # We're immutable, so use __new__ not __init__ - def __new__(cls, numerator=0, denominator=None, *, _normalize=True): + def __new__(cls, numerator=0, denominator=None): """Constructs a Rational. Takes a string like '3/2' or '1.5', another Rational instance, a @@ -279,12 +279,11 @@ def __new__(cls, numerator=0, denominator=None, *, _normalize=True): if denominator == 0: raise ZeroDivisionError('Fraction(%s, 0)' % numerator) - if _normalize: - g = math.gcd(numerator, denominator) - if denominator < 0: - g = -g - numerator //= g - denominator //= g + g = math.gcd(numerator, denominator) + if denominator < 0: + g = -g + numerator //= g + denominator //= g self._numerator = numerator self._denominator = denominator return self From 4e8977af55780ef517d8600719caaf6c2e0d23d9 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Mon, 27 Feb 2023 06:21:15 +0300 Subject: [PATCH 08/11] Rename: _from_pair -> _from_coprime_ints --- Lib/fractions.py | 44 +++++++++++++++++----------------- Lib/test/test_numeric_tower.py | 2 +- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/Lib/fractions.py b/Lib/fractions.py index 3d332194521227..08fe99962c84d0 100644 --- a/Lib/fractions.py +++ b/Lib/fractions.py @@ -300,7 +300,7 @@ def from_float(cls, f): elif not isinstance(f, float): raise TypeError("%s.from_float() only takes floats, not %r (%s)" % (cls.__name__, f, type(f).__name__)) - return cls._from_pair(*f.as_integer_ratio()) + return cls._from_coprime_ints(*f.as_integer_ratio()) @classmethod def from_decimal(cls, dec): @@ -312,10 +312,10 @@ def from_decimal(cls, dec): raise TypeError( "%s.from_decimal() only takes Decimals, not %r (%s)" % (cls.__name__, dec, type(dec).__name__)) - return cls._from_pair(*dec.as_integer_ratio()) + return cls._from_coprime_ints(*dec.as_integer_ratio()) @classmethod - def _from_pair(cls, numerator, denominator, /): + def _from_coprime_ints(cls, numerator, denominator, /): """Convert a pair of ints to a rational number, for internal use. The ratio of integers should be in lowest terms and @@ -391,9 +391,9 @@ def limit_denominator(self, max_denominator=1000000): # the distance from p1/q1 to self is d/(q1*self._denominator). So we # need to compare 2*(q0+k*q1) with self._denominator/d. if 2*d*(q0+k*q1) <= self._denominator: - return Fraction._from_pair(p1, q1) + return Fraction._from_coprime_ints(p1, q1) else: - return Fraction._from_pair(p0+k*p1, q0+k*q1) + return Fraction._from_coprime_ints(p0+k*p1, q0+k*q1) @property def numerator(a): @@ -714,13 +714,13 @@ def _add(a, b): nb, db = b._numerator, b._denominator g = math.gcd(da, db) if g == 1: - return Fraction._from_pair(na * db + da * nb, da * db) + return Fraction._from_coprime_ints(na * db + da * nb, da * db) s = da // g t = na * (db // g) + nb * s g2 = math.gcd(t, g) if g2 == 1: - return Fraction._from_pair(t, s * db) - return Fraction._from_pair(t // g2, s * (db // g2)) + return Fraction._from_coprime_ints(t, s * db) + return Fraction._from_coprime_ints(t // g2, s * (db // g2)) __add__, __radd__ = _operator_fallbacks(_add, operator.add) @@ -730,13 +730,13 @@ def _sub(a, b): nb, db = b._numerator, b._denominator g = math.gcd(da, db) if g == 1: - return Fraction._from_pair(na * db - da * nb, da * db) + return Fraction._from_coprime_ints(na * db - da * nb, da * db) s = da // g t = na * (db // g) - nb * s g2 = math.gcd(t, g) if g2 == 1: - return Fraction._from_pair(t, s * db) - return Fraction._from_pair(t // g2, s * (db // g2)) + return Fraction._from_coprime_ints(t, s * db) + return Fraction._from_coprime_ints(t // g2, s * (db // g2)) __sub__, __rsub__ = _operator_fallbacks(_sub, operator.sub) @@ -752,7 +752,7 @@ def _mul(a, b): if g2 > 1: nb //= g2 da //= g2 - return Fraction._from_pair(na * nb, db * da) + return Fraction._from_coprime_ints(na * nb, db * da) __mul__, __rmul__ = _operator_fallbacks(_mul, operator.mul) @@ -774,7 +774,7 @@ def _div(a, b): n, d = na * db, nb * da if d < 0: n, d = -n, -d - return Fraction._from_pair(n, d) + return Fraction._from_coprime_ints(n, d) __truediv__, __rtruediv__ = _operator_fallbacks(_div, operator.truediv) @@ -811,17 +811,17 @@ def __pow__(a, b): if b.denominator == 1: power = b.numerator if power >= 0: - return Fraction._from_pair(a._numerator ** power, - a._denominator ** power) + return Fraction._from_coprime_ints(a._numerator ** power, + a._denominator ** power) elif a._numerator > 0: - return Fraction._from_pair(a._denominator ** -power, - a._numerator ** -power) + return Fraction._from_coprime_ints(a._denominator ** -power, + a._numerator ** -power) elif a._numerator == 0: raise ZeroDivisionError('Fraction(%s, 0)' % a._denominator ** -power) else: - return Fraction._from_pair((-a._denominator) ** -power, - (-a._numerator) ** -power) + return Fraction._from_coprime_ints((-a._denominator) ** -power, + (-a._numerator) ** -power) else: # A fractional power will generally produce an # irrational number. @@ -845,15 +845,15 @@ def __rpow__(b, a): def __pos__(a): """+a: Coerces a subclass instance to Fraction""" - return Fraction._from_pair(a._numerator, a._denominator) + return Fraction._from_coprime_ints(a._numerator, a._denominator) def __neg__(a): """-a""" - return Fraction._from_pair(-a._numerator, a._denominator) + return Fraction._from_coprime_ints(-a._numerator, a._denominator) def __abs__(a): """abs(a)""" - return Fraction._from_pair(abs(a._numerator), a._denominator) + return Fraction._from_coprime_ints(abs(a._numerator), a._denominator) def __int__(a, _index=operator.index): """int(a)""" diff --git a/Lib/test/test_numeric_tower.py b/Lib/test/test_numeric_tower.py index af9b3ab7f47d66..337682d6bac96c 100644 --- a/Lib/test/test_numeric_tower.py +++ b/Lib/test/test_numeric_tower.py @@ -145,7 +145,7 @@ def test_fractions(self): # The numbers ABC doesn't enforce that the "true" division # of integers produces a float. This tests that the # Rational.__float__() method has required type conversions. - x = F._from_pair(DummyIntegral(1), DummyIntegral(2)) + x = F._from_coprime_ints(DummyIntegral(1), DummyIntegral(2)) self.assertRaises(TypeError, lambda: x.numerator/x.denominator) self.assertEqual(float(x), 0.5) From 4b4a386bbd8f69bc08d26433315eacbcd7094f52 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Mon, 27 Feb 2023 12:24:26 +0300 Subject: [PATCH 09/11] Update Misc/NEWS.d/next/Library/2023-02-10-11-59-13.gh-issue-101773.J_kI7y.rst Co-authored-by: Pieter Eendebak --- .../next/Library/2023-02-10-11-59-13.gh-issue-101773.J_kI7y.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2023-02-10-11-59-13.gh-issue-101773.J_kI7y.rst b/Misc/NEWS.d/next/Library/2023-02-10-11-59-13.gh-issue-101773.J_kI7y.rst index 1741b4a10ba24b..31341c0e47c538 100644 --- a/Misc/NEWS.d/next/Library/2023-02-10-11-59-13.gh-issue-101773.J_kI7y.rst +++ b/Misc/NEWS.d/next/Library/2023-02-10-11-59-13.gh-issue-101773.J_kI7y.rst @@ -1 +1 @@ -Optimize :class:`fractions.Fraction` for small components. +Optimize :class:`fractions.Fraction` for small components. The private argument `_normalize` of the `Fraction` constructor has been removed. From 580b1be3bb5126ec729badbaf6fc662e45f4fff2 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Mon, 27 Feb 2023 12:34:18 +0300 Subject: [PATCH 10/11] +1 --- .../Library/2023-02-10-11-59-13.gh-issue-101773.J_kI7y.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2023-02-10-11-59-13.gh-issue-101773.J_kI7y.rst b/Misc/NEWS.d/next/Library/2023-02-10-11-59-13.gh-issue-101773.J_kI7y.rst index 31341c0e47c538..b577d93d28c2ae 100644 --- a/Misc/NEWS.d/next/Library/2023-02-10-11-59-13.gh-issue-101773.J_kI7y.rst +++ b/Misc/NEWS.d/next/Library/2023-02-10-11-59-13.gh-issue-101773.J_kI7y.rst @@ -1 +1,2 @@ -Optimize :class:`fractions.Fraction` for small components. The private argument `_normalize` of the `Fraction` constructor has been removed. +Optimize :class:`fractions.Fraction` for small components. The private argument +``_normalize`` of the :class:`fractions.Fraction` constructor has been removed. From 53d5e9407f0588bc2e12f3b919e66a49985be9b9 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Mon, 27 Feb 2023 18:20:06 +0000 Subject: [PATCH 11/11] Tweak docstring wording --- Lib/fractions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/fractions.py b/Lib/fractions.py index 08fe99962c84d0..f718b35639beee 100644 --- a/Lib/fractions.py +++ b/Lib/fractions.py @@ -318,8 +318,8 @@ def from_decimal(cls, dec): def _from_coprime_ints(cls, numerator, denominator, /): """Convert a pair of ints to a rational number, for internal use. - The ratio of integers should be in lowest terms and - the denominator is positive. + The ratio of integers should be in lowest terms and the denominator + should be positive. """ obj = super(Fraction, cls).__new__(cls) obj._numerator = numerator