From 87d6cd3604e5c83c06339276228139f5e040b0e7 Mon Sep 17 00:00:00 2001 From: Ammar Askar Date: Sat, 21 Sep 2019 00:28:49 -0400 Subject: [PATCH] bpo-38237: Make pow's arguments have more descriptive names and be keyword passable (GH-16302) Edit: `math.pow` changes removed on Mark's request. https://bugs.python.org/issue38237 Automerge-Triggered-By: @rhettinger --- Doc/faq/programming.rst | 25 +++++------- Doc/library/functions.rst | 24 ++++++----- Lib/test/test_builtin.py | 13 ++++++ .../2019-09-20-14-27-17.bpo-38237.xRUZbx.rst | 2 + Python/bltinmodule.c | 16 ++++---- Python/clinic/bltinmodule.c.h | 40 +++++++++++-------- 6 files changed, 72 insertions(+), 48 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2019-09-20-14-27-17.bpo-38237.xRUZbx.rst diff --git a/Doc/faq/programming.rst b/Doc/faq/programming.rst index 2ff7236df1d..9d45765abaa 100644 --- a/Doc/faq/programming.rst +++ b/Doc/faq/programming.rst @@ -779,26 +779,23 @@ A slash in the argument list of a function denotes that the parameters prior to it are positional-only. Positional-only parameters are the ones without an externally-usable name. Upon calling a function that accepts positional-only parameters, arguments are mapped to parameters based solely on their position. -For example, :func:`pow` is a function that accepts positional-only parameters. -Its documentation looks like this:: +For example, :func:`divmod` is a function that accepts positional-only +parameters. Its documentation looks like this:: - >>> help(pow) - Help on built-in function pow in module builtins: + >>> help(divmod) + Help on built-in function divmod in module builtins: - pow(x, y, z=None, /) - Equivalent to x**y (with two arguments) or x**y % z (with three arguments) + divmod(x, y, /) + Return the tuple (x//y, x%y). Invariant: div*y + mod == x. - Some types, such as ints, are able to use a more efficient algorithm when - invoked using the three argument form. +The slash at the end of the parameter list means that both parameters are +positional-only. Thus, calling :func:`divmod` with keyword arguments would lead +to an error:: -The slash at the end of the parameter list means that all three parameters are -positional-only. Thus, calling :func:`pow` with keyword arguments would lead to -an error:: - - >>> pow(x=3, y=4) + >>> divmod(x=3, y=4) Traceback (most recent call last): File "", line 1, in - TypeError: pow() takes no keyword arguments + TypeError: divmod() takes no keyword arguments Numbers and strings diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index ea2777f7536..4d3b2f593c0 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -1274,11 +1274,12 @@ are always available. They are listed here in alphabetical order. returns ``8364``. This is the inverse of :func:`chr`. -.. function:: pow(x, y[, z]) +.. function:: pow(base, exp[, mod]) - Return *x* to the power *y*; if *z* is present, return *x* to the power *y*, - modulo *z* (computed more efficiently than ``pow(x, y) % z``). The two-argument - form ``pow(x, y)`` is equivalent to using the power operator: ``x**y``. + Return *base* to the power *exp*; if *mod* is present, return *base* to the + power *exp*, modulo *mod* (computed more efficiently than + ``pow(base, exp) % mod``). The two-argument form ``pow(base, exp)`` is + equivalent to using the power operator: ``base**exp``. The arguments must have numeric types. With mixed operand types, the coercion rules for binary arithmetic operators apply. For :class:`int` @@ -1287,14 +1288,15 @@ are always available. They are listed here in alphabetical order. converted to float and a float result is delivered. For example, ``10**2`` returns ``100``, but ``10**-2`` returns ``0.01``. - For :class:`int` operands *x* and *y*, if *z* is present, *z* must also be - of integer type and *z* must be nonzero. If *z* is present and *y* is - negative, *x* must be relatively prime to *z*. In that case, ``pow(inv_x, - -y, z)`` is returned, where *inv_x* is an inverse to *x* modulo *z*. + For :class:`int` operands *base* and *exp*, if *mod* is present, *mod* must + also be of integer type and *mod* must be nonzero. If *mod* is present and + *exp* is negative, *base* must be relatively prime to *mod*. In that case, + ``pow(inv_base, -exp, mod)`` is returned, where *inv_base* is an inverse to + *base* modulo *mod*. Here's an example of computing an inverse for ``38`` modulo ``97``:: - >>> pow(38, -1, 97) + >>> pow(38, -1, mod=97) 23 >>> 23 * 38 % 97 == 1 True @@ -1304,6 +1306,10 @@ are always available. They are listed here in alphabetical order. the second argument to be negative, permitting computation of modular inverses. + .. versionchanged:: 3.9 + Allow keyword arguments. Formerly, only positional arguments were + supported. + .. function:: print(*objects, sep=' ', end='\\n', file=sys.stdout, flush=False) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index e1d2fffb36e..abccf322274 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -19,6 +19,7 @@ import types import unittest import warnings from contextlib import ExitStack +from functools import partial from inspect import CO_COROUTINE from itertools import product from textwrap import dedent @@ -1206,6 +1207,18 @@ class BuiltinTest(unittest.TestCase): self.assertRaises(TypeError, pow) + # Test passing in arguments as keywords. + self.assertEqual(pow(0, exp=0), 1) + self.assertEqual(pow(base=2, exp=4), 16) + self.assertEqual(pow(base=5, exp=2, mod=14), 11) + twopow = partial(pow, base=2) + self.assertEqual(twopow(exp=5), 32) + fifth_power = partial(pow, exp=5) + self.assertEqual(fifth_power(2), 32) + mod10 = partial(pow, mod=10) + self.assertEqual(mod10(2, 6), 4) + self.assertEqual(mod10(exp=6, base=2), 4) + def test_input(self): self.write_testfile() fp = open(TESTFN, 'r') diff --git a/Misc/NEWS.d/next/Library/2019-09-20-14-27-17.bpo-38237.xRUZbx.rst b/Misc/NEWS.d/next/Library/2019-09-20-14-27-17.bpo-38237.xRUZbx.rst new file mode 100644 index 00000000000..07e1bf712db --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-09-20-14-27-17.bpo-38237.xRUZbx.rst @@ -0,0 +1,2 @@ +The arguments for the builtin pow function are more descriptive. They can now +also be passed in as keywords. diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index a40eb421f5d..31e7ad7658b 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -1796,22 +1796,22 @@ builtin_ord(PyObject *module, PyObject *c) /*[clinic input] pow as builtin_pow - x: object - y: object - z: object = None - / + base: object + exp: object + mod: object = None -Equivalent to x**y (with two arguments) or x**y % z (with three arguments) +Equivalent to base**exp (with two arguments) or base**exp % mod (with three arguments) Some types, such as ints, are able to use a more efficient algorithm when invoked using the three argument form. [clinic start generated code]*/ static PyObject * -builtin_pow_impl(PyObject *module, PyObject *x, PyObject *y, PyObject *z) -/*[clinic end generated code: output=50a14d5d130d404b input=653d57d38d41fc07]*/ +builtin_pow_impl(PyObject *module, PyObject *base, PyObject *exp, + PyObject *mod) +/*[clinic end generated code: output=3ca1538221bbf15f input=bd72d0a0ec8e5eb5]*/ { - return PyNumber_Power(x, y, z); + return PyNumber_Power(base, exp, mod); } diff --git a/Python/clinic/bltinmodule.c.h b/Python/clinic/bltinmodule.c.h index b936b0cf372..f6c5b7c1c8b 100644 --- a/Python/clinic/bltinmodule.c.h +++ b/Python/clinic/bltinmodule.c.h @@ -608,39 +608,45 @@ PyDoc_STRVAR(builtin_ord__doc__, {"ord", (PyCFunction)builtin_ord, METH_O, builtin_ord__doc__}, PyDoc_STRVAR(builtin_pow__doc__, -"pow($module, x, y, z=None, /)\n" +"pow($module, /, base, exp, mod=None)\n" "--\n" "\n" -"Equivalent to x**y (with two arguments) or x**y % z (with three arguments)\n" +"Equivalent to base**exp (with two arguments) or base**exp % mod (with three arguments)\n" "\n" "Some types, such as ints, are able to use a more efficient algorithm when\n" "invoked using the three argument form."); #define BUILTIN_POW_METHODDEF \ - {"pow", (PyCFunction)(void(*)(void))builtin_pow, METH_FASTCALL, builtin_pow__doc__}, + {"pow", (PyCFunction)(void(*)(void))builtin_pow, METH_FASTCALL|METH_KEYWORDS, builtin_pow__doc__}, static PyObject * -builtin_pow_impl(PyObject *module, PyObject *x, PyObject *y, PyObject *z); +builtin_pow_impl(PyObject *module, PyObject *base, PyObject *exp, + PyObject *mod); static PyObject * -builtin_pow(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +builtin_pow(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; - PyObject *x; - PyObject *y; - PyObject *z = Py_None; + static const char * const _keywords[] = {"base", "exp", "mod", NULL}; + static _PyArg_Parser _parser = {NULL, _keywords, "pow", 0}; + PyObject *argsbuf[3]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 2; + PyObject *base; + PyObject *exp; + PyObject *mod = Py_None; - if (!_PyArg_CheckPositional("pow", nargs, 2, 3)) { + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 3, 0, argsbuf); + if (!args) { goto exit; } - x = args[0]; - y = args[1]; - if (nargs < 3) { - goto skip_optional; + base = args[0]; + exp = args[1]; + if (!noptargs) { + goto skip_optional_pos; } - z = args[2]; -skip_optional: - return_value = builtin_pow_impl(module, x, y, z); + mod = args[2]; +skip_optional_pos: + return_value = builtin_pow_impl(module, base, exp, mod); exit: return return_value; @@ -849,4 +855,4 @@ builtin_issubclass(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=4e118c2cd2cd98f3 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=1e2a6185e05ecd11 input=a9049054013a1b77]*/