Closed
Description
Right now we just use __new__()
with a private kwarg _normalize=False
. That's slightly less efficient, then a dedicated class method.
POC patch
diff --git a/Lib/fractions.py b/Lib/fractions.py
index 49a3f2841a..27c75785fb 100644
--- a/Lib/fractions.py
+++ b/Lib/fractions.py
@@ -315,6 +315,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
@@ -703,13 +710,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)
We can drop private kwarg of __new__()
after adopting this way.
Some benchmarks
With above patch
$ ./python -m timeit -s 'from fractions import Fraction as Q' -s 'a,b=Q(3,7),Q(5, 8)' -u usec 'a+b'
50000 loops, best of 5: 4.4 usec per loop
$ ./python -m timeit -s 'from fractions import Fraction as Q' -s 'a,b=Q(3,7)**100,Q(5, 8)**100' -u usec 'a+b'
20000 loops, best of 5: 12.6 usec per loop
On the mastermain:
$ ./python -m timeit -s 'from fractions import Fraction as Q' -s 'a,b=Q(3,7),Q(5, 8)' -u usec 'a+b'
50000 loops, best of 5: 6.99 usec per loop
$ ./python -m timeit -s 'from fractions import Fraction as Q' -s 'a,b=Q(3,7)**100,Q(5, 8)**100' -u usec 'a+b'
20000 loops, best of 5: 15.9 usec per loop