[Python-checkins] cpython: Issue #21136: Avoid unnecessary normalization in Fractions resulting from power

mark.dickinson python-checkins at python.org
Sat Apr 5 10:29:17 CEST 2014


http://hg.python.org/cpython/rev/91d7fadac271
changeset:   90152:91d7fadac271
user:        Mark Dickinson <dickinsm at gmail.com>
date:        Sat Apr 05 09:29:00 2014 +0100
summary:
  Issue #21136: Avoid unnecessary normalization in Fractions resulting from power and other operations.

files:
  Lib/fractions.py           |  23 ++++++++++++++---------
  Lib/test/test_fractions.py |   3 ++-
  Misc/NEWS                  |   3 +++
  3 files changed, 19 insertions(+), 10 deletions(-)


diff --git a/Lib/fractions.py b/Lib/fractions.py
--- a/Lib/fractions.py
+++ b/Lib/fractions.py
@@ -70,7 +70,7 @@
     __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
@@ -165,9 +165,12 @@
 
         if denominator == 0:
             raise ZeroDivisionError('Fraction(%s, 0)' % numerator)
-        g = gcd(numerator, denominator)
-        self._numerator = numerator // g
-        self._denominator = denominator // g
+        if _normalize:
+            g = gcd(numerator, denominator)
+            numerator //= g
+            denominator //= g
+        self._numerator = numerator
+        self._denominator = denominator
         return self
 
     @classmethod
@@ -453,10 +456,12 @@
                 power = b.numerator
                 if power >= 0:
                     return Fraction(a._numerator ** power,
-                                    a._denominator ** power)
+                                    a._denominator ** power,
+                                    _normalize=False)
                 else:
                     return Fraction(a._denominator ** -power,
-                                    a._numerator ** -power)
+                                    a._numerator ** -power,
+                                    _normalize=False)
             else:
                 # A fractional power will generally produce an
                 # irrational number.
@@ -480,15 +485,15 @@
 
     def __pos__(a):
         """+a: Coerces a subclass instance to Fraction"""
-        return Fraction(a._numerator, a._denominator)
+        return Fraction(a._numerator, a._denominator, _normalize=False)
 
     def __neg__(a):
         """-a"""
-        return Fraction(-a._numerator, a._denominator)
+        return Fraction(-a._numerator, a._denominator, _normalize=False)
 
     def __abs__(a):
         """abs(a)"""
-        return Fraction(abs(a._numerator), a._denominator)
+        return Fraction(abs(a._numerator), a._denominator, _normalize=False)
 
     def __trunc__(a):
         """trunc(a)"""
diff --git a/Lib/test/test_fractions.py b/Lib/test/test_fractions.py
--- a/Lib/test/test_fractions.py
+++ b/Lib/test/test_fractions.py
@@ -330,7 +330,6 @@
         self.assertTypedEquals(F(-2, 10), round(F(-15, 100), 1))
         self.assertTypedEquals(F(-2, 10), round(F(-25, 100), 1))
 
-
     def testArithmetic(self):
         self.assertEqual(F(1, 2), F(1, 10) + F(2, 5))
         self.assertEqual(F(-3, 10), F(1, 10) - F(2, 5))
@@ -402,6 +401,8 @@
         self.assertTypedEquals(2.0 , 4 ** F(1, 2))
         self.assertTypedEquals(0.25, 2.0 ** F(-2, 1))
         self.assertTypedEquals(1.0 + 0j, (1.0 + 0j) ** F(1, 10))
+        self.assertRaises(ZeroDivisionError, operator.pow,
+                          F(0, 1), -2)
 
     def testMixingWithDecimal(self):
         # Decimal refuses mixed arithmetic (but not mixed comparisons)
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -29,6 +29,9 @@
 Library
 -------
 
+- Issue #21136: Avoid unnecessary normalization of Fractions resulting from
+  power and other operations.  Patch by Raymond Hettinger.
+
 - Issue #17621: Introduce importlib.util.LazyLoader.
 
 - Issue #21076: signal module constants were turned into enums.

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list