-
- Notifications
You must be signed in to change notification settings - Fork 33.7k
gh-82017: Support as_integer_ratio() in the Fraction constructor #120271
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
62bb662 de5b423 adf4869 68bb174 4ff41e4 88bdd20 ab9dc17 b0b5444 07bcf81 1539af5 f1c4409 2ace711 File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| | @@ -3,7 +3,6 @@ | |||||
| | ||||||
| """Fraction, infinite-precision, rational numbers.""" | ||||||
| | ||||||
| from decimal import Decimal | ||||||
| import functools | ||||||
| import math | ||||||
| import numbers | ||||||
| | @@ -244,7 +243,8 @@ def __new__(cls, numerator=0, denominator=None): | |||||
| self._denominator = numerator.denominator | ||||||
| return self | ||||||
| | ||||||
| elif isinstance(numerator, (float, Decimal)): | ||||||
| elif (isinstance(numerator, numbers.Number) and | ||||||
| ||||||
| elif (isinstance(numerator, numbers.Number) and | |
| elif (isinstance(numerator, (float, Decimal, numbers.Number)) and |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would like to avoid importing decimal. This is a side benefit of this PR. But adding a fast path for float may be worthwhile.
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, but this looks wrong for me. While as_integer_ratio() methods for builtin types have ratio in lowest terms and with a positive denominator, we have no such constraint. It's possible to create unnormalized fractions, but some Fraction methods work incorrectly for such input:
>>> from fractions import Fraction as F >>> import numbers >>> class R: ... def __init__(self, ratio): ... self._ratio = ratio ... def as_integer_ratio(self): ... return self._ratio ... >>> numbers.Number.register(R) <class '__main__.R'> >>> F(R((6, 4))) Fraction(6, 4) >>> F(R((6, 4))) + F(R((2, 2))) Fraction(5, 2) >>> 1/F(R((6, 4))) Fraction(4, 6) >>> F(R((6, 4))) == F(3, 2) False >>> F(R((6, 4))).limit_denominator(3) Traceback (most recent call last): File "<python-input-7>", line 1, in <module> F(R((6, 4))).limit_denominator(3) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^ File "/home/sk/src/cpython/Lib/fractions.py", line 397, in limit_denominator a = n//d ~^^~ ZeroDivisionError: division by zeroPlease either normalize input or mention requirements for as_integer_ratio() in docs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer to update the documentation. For builtin types calling normalization would be a waste of time.
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| | @@ -355,6 +355,29 @@ def testInitFromDecimal(self): | |||||||||
| self.assertRaises(OverflowError, F, Decimal('inf')) | ||||||||||
| self.assertRaises(OverflowError, F, Decimal('-inf')) | ||||||||||
| | ||||||||||
| def testInitFromIntegerRatio(self): | ||||||||||
| class Ratio: | ||||||||||
| def __init__(self, ratio): | ||||||||||
| self._ratio = ratio | ||||||||||
| def as_integer_ratio(self): | ||||||||||
| return self._ratio | ||||||||||
| class RatioNumber(Ratio): | ||||||||||
| pass | ||||||||||
| numbers.Number.register(RatioNumber) | ||||||||||
| | ||||||||||
| self.assertEqual((7, 3), _components(F(RatioNumber((7, 3))))) | ||||||||||
| ||||||||||
| self.assertEqual((7, 3), _components(F(RatioNumber((7, 3))))) | |
| # as_integer_ratio() output should be normalized; lets check that | |
| # we just keep this unmodified | |
| self.assertEqual((6, 4), _components(F(RatioNumber((6, 4))))) |
Forgot that. I think, this should address Victor's note.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is invalid ratio. The result is undefined.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is invalid ratio.
Yeah, but test just checks that we don't touch as_integer_ratio() output.
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a test on a fraction which can be simplified, to test "It returns a :class:Fraction instance with exactly the same value." from the doc? For example, the ratio 6/4. (Test that the GCD is not computed to simplify the ratio.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it is an illegal input. Existing implementations return a simple ratio, simplifying it or checking that it is simplified would be just a waste of time.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| Added support for converting any numbers that have the | ||
| :meth:`!as_integer_ratio` method to a :class:`~fractions.Fraction`. |
Uh oh!
There was an error while loading. Please reload this page.