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
This commit is contained in:
Ammar Askar 2019-09-21 00:28:49 -04:00 committed by Miss Islington (bot)
parent e267793aa4
commit 87d6cd3604
6 changed files with 72 additions and 48 deletions

View file

@ -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 it are positional-only. Positional-only parameters are the ones without an
externally-usable name. Upon calling a function that accepts positional-only externally-usable name. Upon calling a function that accepts positional-only
parameters, arguments are mapped to parameters based solely on their position. parameters, arguments are mapped to parameters based solely on their position.
For example, :func:`pow` is a function that accepts positional-only parameters. For example, :func:`divmod` is a function that accepts positional-only
Its documentation looks like this:: parameters. Its documentation looks like this::
>>> help(pow) >>> help(divmod)
Help on built-in function pow in module builtins: Help on built-in function divmod in module builtins:
pow(x, y, z=None, /) divmod(x, y, /)
Equivalent to x**y (with two arguments) or x**y % z (with three arguments) 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 The slash at the end of the parameter list means that both parameters are
invoked using the three argument form. 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 >>> divmod(x=3, y=4)
positional-only. Thus, calling :func:`pow` with keyword arguments would lead to
an error::
>>> pow(x=3, y=4)
Traceback (most recent call last): Traceback (most recent call last):
File "<stdin>", line 1, in <module> File "<stdin>", line 1, in <module>
TypeError: pow() takes no keyword arguments TypeError: divmod() takes no keyword arguments
Numbers and strings Numbers and strings

View file

@ -1274,11 +1274,12 @@ are always available. They are listed here in alphabetical order.
returns ``8364``. This is the inverse of :func:`chr`. 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*, Return *base* to the power *exp*; if *mod* is present, return *base* to the
modulo *z* (computed more efficiently than ``pow(x, y) % z``). The two-argument power *exp*, modulo *mod* (computed more efficiently than
form ``pow(x, y)`` is equivalent to using the power operator: ``x**y``. ``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 The arguments must have numeric types. With mixed operand types, the
coercion rules for binary arithmetic operators apply. For :class:`int` 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`` converted to float and a float result is delivered. For example, ``10**2``
returns ``100``, but ``10**-2`` returns ``0.01``. returns ``100``, but ``10**-2`` returns ``0.01``.
For :class:`int` operands *x* and *y*, if *z* is present, *z* must also be For :class:`int` operands *base* and *exp*, if *mod* is present, *mod* must
of integer type and *z* must be nonzero. If *z* is present and *y* is also be of integer type and *mod* must be nonzero. If *mod* is present and
negative, *x* must be relatively prime to *z*. In that case, ``pow(inv_x, *exp* is negative, *base* must be relatively prime to *mod*. In that case,
-y, z)`` is returned, where *inv_x* is an inverse to *x* modulo *z*. ``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``:: Here's an example of computing an inverse for ``38`` modulo ``97``::
>>> pow(38, -1, 97) >>> pow(38, -1, mod=97)
23 23
>>> 23 * 38 % 97 == 1 >>> 23 * 38 % 97 == 1
True 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 the second argument to be negative, permitting computation of modular
inverses. inverses.
.. versionchanged:: 3.9
Allow keyword arguments. Formerly, only positional arguments were
supported.
.. function:: print(*objects, sep=' ', end='\\n', file=sys.stdout, flush=False) .. function:: print(*objects, sep=' ', end='\\n', file=sys.stdout, flush=False)

View file

@ -19,6 +19,7 @@ import types
import unittest import unittest
import warnings import warnings
from contextlib import ExitStack from contextlib import ExitStack
from functools import partial
from inspect import CO_COROUTINE from inspect import CO_COROUTINE
from itertools import product from itertools import product
from textwrap import dedent from textwrap import dedent
@ -1206,6 +1207,18 @@ class BuiltinTest(unittest.TestCase):
self.assertRaises(TypeError, pow) 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): def test_input(self):
self.write_testfile() self.write_testfile()
fp = open(TESTFN, 'r') fp = open(TESTFN, 'r')

View file

@ -0,0 +1,2 @@
The arguments for the builtin pow function are more descriptive. They can now
also be passed in as keywords.

View file

@ -1796,22 +1796,22 @@ builtin_ord(PyObject *module, PyObject *c)
/*[clinic input] /*[clinic input]
pow as builtin_pow pow as builtin_pow
x: object base: object
y: object exp: object
z: object = None 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 Some types, such as ints, are able to use a more efficient algorithm when
invoked using the three argument form. invoked using the three argument form.
[clinic start generated code]*/ [clinic start generated code]*/
static PyObject * static PyObject *
builtin_pow_impl(PyObject *module, PyObject *x, PyObject *y, PyObject *z) builtin_pow_impl(PyObject *module, PyObject *base, PyObject *exp,
/*[clinic end generated code: output=50a14d5d130d404b input=653d57d38d41fc07]*/ PyObject *mod)
/*[clinic end generated code: output=3ca1538221bbf15f input=bd72d0a0ec8e5eb5]*/
{ {
return PyNumber_Power(x, y, z); return PyNumber_Power(base, exp, mod);
} }

View file

@ -608,39 +608,45 @@ PyDoc_STRVAR(builtin_ord__doc__,
{"ord", (PyCFunction)builtin_ord, METH_O, builtin_ord__doc__}, {"ord", (PyCFunction)builtin_ord, METH_O, builtin_ord__doc__},
PyDoc_STRVAR(builtin_pow__doc__, PyDoc_STRVAR(builtin_pow__doc__,
"pow($module, x, y, z=None, /)\n" "pow($module, /, base, exp, mod=None)\n"
"--\n" "--\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" "\n"
"Some types, such as ints, are able to use a more efficient algorithm when\n" "Some types, such as ints, are able to use a more efficient algorithm when\n"
"invoked using the three argument form."); "invoked using the three argument form.");
#define BUILTIN_POW_METHODDEF \ #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 * 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 * 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 *return_value = NULL;
PyObject *x; static const char * const _keywords[] = {"base", "exp", "mod", NULL};
PyObject *y; static _PyArg_Parser _parser = {NULL, _keywords, "pow", 0};
PyObject *z = Py_None; 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; goto exit;
} }
x = args[0]; base = args[0];
y = args[1]; exp = args[1];
if (nargs < 3) { if (!noptargs) {
goto skip_optional; goto skip_optional_pos;
} }
z = args[2]; mod = args[2];
skip_optional: skip_optional_pos:
return_value = builtin_pow_impl(module, x, y, z); return_value = builtin_pow_impl(module, base, exp, mod);
exit: exit:
return return_value; return return_value;
@ -849,4 +855,4 @@ builtin_issubclass(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
exit: exit:
return return_value; return return_value;
} }
/*[clinic end generated code: output=4e118c2cd2cd98f3 input=a9049054013a1b77]*/ /*[clinic end generated code: output=1e2a6185e05ecd11 input=a9049054013a1b77]*/