Issue #5812: The two-argument form of the Fraction constructor

now accepts arbitrary Rational instances.
This commit is contained in:
Mark Dickinson 2009-04-24 13:56:07 +00:00
parent 90d47cb46c
commit 4af8e745c4
3 changed files with 32 additions and 13 deletions

View File

@ -56,7 +56,7 @@ class Fraction(Rational):
__slots__ = ('_numerator', '_denominator')
# We're immutable, so use __new__ not __init__
def __new__(cls, numerator=0, denominator=1):
def __new__(cls, numerator=0, denominator=None):
"""Constructs a Fraction.
Takes a string like '3/2' or '1.5', another Fraction, or a
@ -65,8 +65,13 @@ class Fraction(Rational):
"""
self = super(Fraction, cls).__new__(cls)
if type(numerator) not in (int, long) and denominator == 1:
if isinstance(numerator, basestring):
if denominator is None:
if isinstance(numerator, Rational):
self._numerator = numerator.numerator
self._denominator = numerator.denominator
return self
elif isinstance(numerator, basestring):
# Handle construction from strings.
m = _RATIONAL_FORMAT.match(numerator)
if m is None:
@ -93,18 +98,22 @@ class Fraction(Rational):
if m.group('sign') == '-':
numerator = -numerator
elif isinstance(numerator, Rational):
# Handle copies from other rationals. Integrals get
# caught here too, but it doesn't matter because
# denominator is already 1.
other_rational = numerator
numerator = other_rational.numerator
denominator = other_rational.denominator
else:
raise TypeError("argument should be a string "
"or a Rational instance")
elif (isinstance(numerator, Rational) and
isinstance(denominator, Rational)):
numerator, denominator = (
numerator.numerator * denominator.denominator,
denominator.numerator * numerator.denominator
)
else:
raise TypeError("both arguments should be "
"Rational instances")
if denominator == 0:
raise ZeroDivisionError('Fraction(%s, 0)' % numerator)
numerator = operator.index(numerator)
denominator = operator.index(denominator)
g = gcd(numerator, denominator)
self._numerator = numerator // g
self._denominator = denominator // g

View File

@ -60,13 +60,19 @@ class FractionTest(unittest.TestCase):
self.assertEquals((7, 15), _components(F(7, 15)))
self.assertEquals((10**23, 1), _components(F(10**23)))
self.assertEquals((3, 77), _components(F(F(3, 7), 11)))
self.assertEquals((-9, 5), _components(F(2, F(-10, 9))))
self.assertEquals((2486, 2485), _components(F(F(22, 7), F(355, 113))))
self.assertRaisesMessage(ZeroDivisionError, "Fraction(12, 0)",
F, 12, 0)
self.assertRaises(TypeError, F, 1.5)
self.assertRaises(TypeError, F, 1.5 + 3j)
self.assertRaises(TypeError, F, F(1, 2), 3)
self.assertRaises(TypeError, F, "3/2", 3)
self.assertRaises(TypeError, F, 3, 0j)
self.assertRaises(TypeError, F, 3, 1j)
def testFromString(self):
self.assertEquals((5, 1), _components(F("5")))

View File

@ -244,6 +244,10 @@ Core and Builtins
Library
-------
- Issue #5812: For the two-argument form of the Fraction constructor,
Fraction(m, n), m and n are permitted to be arbitrary Rational
instances.
- Issue #5812: Fraction('1e6') is valid: more generally, any string
that's valid for float() is now valid for Fraction(), with the
exception of strings representing NaNs and infinities.