-
-
Notifications
You must be signed in to change notification settings - Fork 32.4k
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
Any numbers that have the as_integer_ratio() method (e.g. numpy.float128) can now be converted to a fraction.
- Loading branch information
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 | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I cannot comment on the TypeError below but you should update: raise TypeError("argument should be a string or a Rational instance") since you do not need a rational only (https://github.com/python/cpython/pull/120271/files#diff-f561eb7eb97f832b2698837f52c2c2cf23bdb0b31c69cf1f6aaa560280993316R281-R282). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Performance nitpick. isinstance checks for abc classes are slow:
In main:
In this branch:
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would like to avoid importing |
||||||
hasattr(numerator, 'as_integer_ratio')): | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, but this looks wrong for me. While >>> 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 zero Please 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 commentThe 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. |
||||||
# Exact conversion | ||||||
self._numerator, self._denominator = numerator.as_integer_ratio() | ||||||
return self | ||||||
|
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))))) | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Forgot that. I think, this should address Victor's note. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 commentThe reason will be displayed to describe this comment to others. Learn more.
Yeah, but test just checks that we don't touch as_integer_ratio() output. |
||||||||||
# not a number | ||||||||||
self.assertRaises(TypeError, F, Ratio((7, 3))) | ||||||||||
# the type also has an "as_integer_ratio" attribute. | ||||||||||
self.assertRaises(TypeError, F, RatioNumber) | ||||||||||
# bad ratio | ||||||||||
self.assertRaises(TypeError, F, RatioNumber(7)) | ||||||||||
self.assertRaises(ValueError, F, RatioNumber((7,))) | ||||||||||
self.assertRaises(ValueError, F, RatioNumber((7, 3, 1))) | ||||||||||
# only single-argument form | ||||||||||
self.assertRaises(TypeError, F, RatioNumber((3, 7)), 11) | ||||||||||
self.assertRaises(TypeError, F, 2, RatioNumber((-10, 9))) | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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: There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||||||||||
|
||||||||||
def testFromString(self): | ||||||||||
self.assertEqual((5, 1), _components(F("5"))) | ||||||||||
self.assertEqual((3, 2), _components(F("3/2"))) | ||||||||||
|
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.