classification
Title: as_integer_ratio() missing from fractions.Fraction()
Type: enhancement Stage: resolved
Components: Library (Lib) Versions: Python 3.9, Python 3.8
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: Nosy List: CuriousLearner, gvanrossum, jdemeyer, lisroach, lukasz.langa, mark.dickinson, rhettinger, scoder, serhiy.storchaka
Priority: high Keywords: easy, patch

Created on 2019-08-11 07:01 by rhettinger, last changed 2019-08-19 09:17 by jdemeyer. This issue is now closed.

Pull Requests
URL Status Linked Edit
PR 15212 closed rhettinger, 2019-08-11 17:38
PR 15215 merged miss-islington, 2019-08-11 21:41
Messages (11)
msg349378 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2019-08-11 07:01
When working on Whatsnew3.8, I noticed that as_integer_ratio() had been added to ints and bools, was already present in floats and Decimals, but was missing from Fractions.  

IIRC, the goal was to make all of these have as a similar API so that x.as_integer_ratio() would work for all types that could support it (not complex numbers).
msg349381 - (view) Author: Stefan Behnel (scoder) * (Python committer) Date: 2019-08-11 08:53
FWIW, makes total sense to me to have it there. Question is more if we can still get it into Py3.8, since it's a new feature for fractions.
msg349397 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2019-08-11 16:41
This will not solve the problem completely, because other rational numbers do not have as_integer_ratio().

But all rational numbers have the numerator and denominator properties. See issue37822 for more general solution.
msg349398 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2019-08-11 16:54
I think this can still go in for Fractions because it completes a feature that was already started (providing the method for the concrete types where it makes sense).

Altering numbers.py can be saved for the future.
msg349403 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2019-08-11 18:00
Guido, would you like to go forward with this?

IIRC, you had decided that adding as_integer_ratio() to all the relevant concrete classes was the way to go.  We already had the method on floats and Decimals, and then it was added bool and float, so Fraction is the only one missing.  I believe the goal was to get x.as_integer_ratio() to run without type testing on all concrete numeric types that could support it.

Serhiy is proposing to instead add a math.as_integer_ratio() function that would extract the components differently for different types (see issue 37822). If that had been our plan, then it was a mistake to add as_integer_ratio() to int and bool (as they already have numerator and denominator attributes). If you change you mind and want to go with 37822, we should probably rip-out the 3.8 addition to int and bool.

Jeroen is proposing to down yet another path for this (see issue 28716). He wants to add a __ratio__ method to all the classes and access them with an operator.ratio() function.  He thinks this will help the SageMath package.

My recommendation is to stick with the original plan of adding the as_integer_ratio() to all the concrete types.  The only one left to be done is in the Fractions module.  That is pretty easy -- see PR 15212.

There is some question about whether to change numbers.Rational() but that discussion can be left for another day -- concrete classes are allowed to have extra methods that aren't in the abstract classes.   The abstract classes are harder to change because any existing user classes that had registered as Rational would instantly be non-compliant (though the fix is easy).

I would like to get this finished up for 3.8.  It doesn't make sense to me to have as_integer_ratio() for bool, int, float, and Decimal but to have omitted the most obvious case, Fraction.  That defeats the purpose of having parallel APIs.
msg349404 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2019-08-11 18:16
Let's continue on the current path -- add Fraction.as_integer_ratio().

Note that as_integer_ratio() is not part of the Numbers API, it is an optional protocol.

You can count me out for Jeroen's __ratio__ proposal.
msg349411 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2019-08-11 21:41
New changeset f03b4c8a48f62134799d368b78da35301af466a3 by Raymond Hettinger in branch 'master':
bpo-37819: Add Fraction.as_integer_ratio() (GH-15212)
https://github.com/python/cpython/commit/f03b4c8a48f62134799d368b78da35301af466a3
msg349412 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2019-08-11 22:02
New changeset 5ba1cb03939bd86d026db667580f590966b573ea by Raymond Hettinger (Miss Islington (bot)) in branch '3.8':
bpo-37819: Add Fraction.as_integer_ratio() (GH-15212) (GH-15215)
https://github.com/python/cpython/commit/5ba1cb03939bd86d026db667580f590966b573ea
msg349413 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2019-08-11 22:03
Thanks.
msg349936 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2019-08-19 05:37
Sorry, but I do not understand why adding Fraction.as_integer_ratio() prevents adding math.as_integer_ratio().

The user code can not benefit from this until we add as_integer_ratio() to all numeric numbers, and this is not realistic. For the same reason there is str.join() which works with arbitrary iterable instead of adding the join() method to all collections, iterators and generators.

math.as_integer_ratio() makes the user code more general, clear and fast.
msg349946 - (view) Author: Jeroen Demeyer (jdemeyer) * (Python triager) Date: 2019-08-19 09:17
> Sorry, but I do not understand why adding Fraction.as_integer_ratio() prevents adding math.as_integer_ratio().

I also support a public function for that. It seems that we're planning this "as_integer_ratio" thing to become public API, so why not have a function as Serhiy proposes?

I consider the situation with as_integer_ratio() very analogous to __index__ where we have operator.index(), so I would actually suggest operator.as_integer_ratio() but that's bikeshedding territory.
History
Date User Action Args
2019-08-19 09:17:27jdemeyersetnosy: + jdemeyer
messages: + msg349946
2019-08-19 05:37:40serhiy.storchakasetmessages: + msg349936
2019-08-11 22:03:01rhettingersetstatus: open -> closed
resolution: fixed
messages: + msg349413

stage: patch review -> resolved
2019-08-11 22:02:28rhettingersetmessages: + msg349412
2019-08-11 21:41:10miss-islingtonsetpull_requests: + pull_request14943
2019-08-11 21:41:02rhettingersetmessages: + msg349411
2019-08-11 18:16:52gvanrossumsetmessages: + msg349404
2019-08-11 18:00:16rhettingersetnosy: + gvanrossum
messages: + msg349403
2019-08-11 17:38:02rhettingersetkeywords: + patch
stage: patch review
pull_requests: + pull_request14940
2019-08-11 16:54:06rhettingersetmessages: + msg349398
2019-08-11 16:41:19serhiy.storchakasetnosy: + serhiy.storchaka
messages: + msg349397
2019-08-11 08:53:30scodersetnosy: + lukasz.langa, scoder
messages: + msg349381
2019-08-11 07:17:44CuriousLearnersetnosy: + CuriousLearner
2019-08-11 07:01:53rhettingercreate