diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index fd59a5170da..5a133e32fce 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -537,6 +537,14 @@ class`. In addition, it provides a few more methods: .. versionadded:: 3.2 +.. method:: int.as_integer_ratio() + + Return a pair of integers whose ratio is exactly equal to the original + integer and with a positive denominator. The integer ratio of integers + (whole numbers) is always the integer as the numerator and ``1`` as the + denominator. + + .. versionadded:: 3.8 Additional Methods on Float --------------------------- diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst index b2475c7df33..38b8623dddd 100644 --- a/Doc/whatsnew/3.8.rst +++ b/Doc/whatsnew/3.8.rst @@ -91,6 +91,10 @@ Other Language Changes was lifted. (Contributed by Serhiy Storchaka in :issue:`32489`.) +* The ``int`` type now has a new ``as_integer_ratio`` method compatible + with the existing ``float.as_integer_ratio`` method. + (Contributed by Lisa Roach in :issue:`33073`.) + * Added support of ``\N{name}`` escapes in :mod:`regular expressions `. (Contributed by Jonathan Eunice and Serhiy Storchaka in :issue:`30688`.) diff --git a/Lib/test/test_doctest.py b/Lib/test/test_doctest.py index 83941c129f4..797bdb84719 100644 --- a/Lib/test/test_doctest.py +++ b/Lib/test/test_doctest.py @@ -665,7 +665,7 @@ plain ol' Python and is guaranteed to be available. True >>> real_tests = [t for t in tests if len(t.examples) > 0] >>> len(real_tests) # objects that actually have doctests - 8 + 9 >>> for t in real_tests: ... print('{} {}'.format(len(t.examples), t.name)) ... @@ -675,6 +675,7 @@ plain ol' Python and is guaranteed to be available. 2 builtins.float.hex 1 builtins.hex 1 builtins.int + 3 builtins.int.as_integer_ratio 2 builtins.int.bit_length 1 builtins.oct diff --git a/Lib/test/test_long.py b/Lib/test/test_long.py index 8472889d48b..7c883baebb4 100644 --- a/Lib/test/test_long.py +++ b/Lib/test/test_long.py @@ -3,6 +3,7 @@ from test import support import sys +import enum import random import math import array @@ -1349,6 +1350,37 @@ class LongTest(unittest.TestCase): self.assertEqual(type(value << shift), int) self.assertEqual(type(value >> shift), int) + def test_as_integer_ratio(self): + tests = [10, 0, -10, 1] + for value in tests: + numerator, denominator = value.as_integer_ratio() + self.assertEqual((numerator, denominator), (value, 1)) + self.assertIsInstance(numerator, int) + self.assertIsInstance(denominator, int) + + def test_as_integer_ratio_maxint(self): + x = sys.maxsize + 1 + self.assertEqual(x.as_integer_ratio()[0], x) + + def test_as_integer_ratio_bool(self): + self.assertEqual(True.as_integer_ratio(), (1, 1)) + self.assertEqual(False.as_integer_ratio(), (0, 1)) + self.assertEqual(type(True.as_integer_ratio()[0]), int) + self.assertEqual(type(False.as_integer_ratio()[0]), int) + + def test_as_integer_ratio_int_enum(self): + class Foo(enum.IntEnum): + X = 42 + self.assertEqual(Foo.X.as_integer_ratio(), (42, 1)) + self.assertEqual(type(Foo.X.as_integer_ratio()[0]), int) + + def test_as_integer_ratio_int_flag(self): + class Foo(enum.IntFlag): + R = 1 << 2 + self.assertEqual(Foo.R.as_integer_ratio(), (4, 1)) + self.assertEqual(type(Foo.R.as_integer_ratio()[0]), int) + + if __name__ == "__main__": unittest.main() diff --git a/Misc/ACKS b/Misc/ACKS index 96985358e23..a29ff6020bb 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1350,6 +1350,7 @@ Juan M. Bello Rivas Mohd Sanad Zaki Rizvi Davide Rizzo Anthony Roach +Lisa Roach Carl Robben Ben Roberts Mark Roberts diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-08-12-16-03-58.bpo-33073.XWu1Jh.rst b/Misc/NEWS.d/next/Core and Builtins/2018-08-12-16-03-58.bpo-33073.XWu1Jh.rst new file mode 100644 index 00000000000..ce9b6129f96 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2018-08-12-16-03-58.bpo-33073.XWu1Jh.rst @@ -0,0 +1 @@ +Added as_integer_ratio to ints to make them more interoperable with floats. diff --git a/Objects/clinic/longobject.c.h b/Objects/clinic/longobject.c.h index 14f5515c7a6..0e70fe5d8c4 100644 --- a/Objects/clinic/longobject.c.h +++ b/Objects/clinic/longobject.c.h @@ -118,6 +118,34 @@ int_bit_length(PyObject *self, PyObject *Py_UNUSED(ignored)) return int_bit_length_impl(self); } +PyDoc_STRVAR(int_as_integer_ratio__doc__, +"as_integer_ratio($self, /)\n" +"--\n" +"\n" +"Return integer ratio.\n" +"\n" +"Return a pair of integers, whose ratio is exactly equal to the original int\n" +"and with a positive denominator.\n" +"\n" +">>> (10).as_integer_ratio()\n" +"(10, 1)\n" +">>> (-10).as_integer_ratio()\n" +"(-10, 1)\n" +">>> (0).as_integer_ratio()\n" +"(0, 1)"); + +#define INT_AS_INTEGER_RATIO_METHODDEF \ + {"as_integer_ratio", (PyCFunction)int_as_integer_ratio, METH_NOARGS, int_as_integer_ratio__doc__}, + +static PyObject * +int_as_integer_ratio_impl(PyObject *self); + +static PyObject * +int_as_integer_ratio(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return int_as_integer_ratio_impl(self); +} + PyDoc_STRVAR(int_to_bytes__doc__, "to_bytes($self, /, length, byteorder, *, signed=False)\n" "--\n" @@ -211,4 +239,4 @@ int_from_bytes(PyTypeObject *type, PyObject *const *args, Py_ssize_t nargs, PyOb exit: return return_value; } -/*[clinic end generated code: output=fd64beb83bd16df3 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=6d5e92d7dc803751 input=a9049054013a1b77]*/ diff --git a/Objects/longobject.c b/Objects/longobject.c index 399d3542709..98ff9a8c265 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -5260,6 +5260,36 @@ long_is_finite(PyObject *v) } #endif +/*[clinic input] +int.as_integer_ratio + +Return integer ratio. + +Return a pair of integers, whose ratio is exactly equal to the original int +and with a positive denominator. + +>>> (10).as_integer_ratio() +(10, 1) +>>> (-10).as_integer_ratio() +(-10, 1) +>>> (0).as_integer_ratio() +(0, 1) +[clinic start generated code]*/ + +static PyObject * +int_as_integer_ratio_impl(PyObject *self) +/*[clinic end generated code: output=e60803ae1cc8621a input=55ce3058e15de393]*/ +{ + if PyLong_CheckExact(self) { + return PyTuple_Pack(2, self, _PyLong_One); + } else { + PyObject *numerator = _PyLong_Copy(self); + PyObject *ratio_tuple = PyTuple_Pack(2, numerator, _PyLong_One); + Py_DECREF(numerator); + return ratio_tuple; + } +} + /*[clinic input] int.to_bytes @@ -5392,6 +5422,7 @@ static PyMethodDef long_methods[] = { #endif INT_TO_BYTES_METHODDEF INT_FROM_BYTES_METHODDEF + INT_AS_INTEGER_RATIO_METHODDEF {"__trunc__", long_long_meth, METH_NOARGS, "Truncating an Integral returns itself."}, {"__floor__", long_long_meth, METH_NOARGS,