[Python-checkins] bpo-38237: Let pow() support keyword arguments (GH-16302) (GH-16320)

Raymond Hettinger webhook-mailer at python.org
Sat Sep 21 04:22:38 EDT 2019


https://github.com/python/cpython/commit/37bc93552375cb1bc616927b5c1905bae3c0e99d
commit: 37bc93552375cb1bc616927b5c1905bae3c0e99d
branch: 3.8
author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com>
committer: Raymond Hettinger <rhettinger at users.noreply.github.com>
date: 2019-09-21T01:22:29-07:00
summary:

bpo-38237: Let pow() support keyword arguments (GH-16302) (GH-16320)

Backported with release manager approval

files:
A Misc/NEWS.d/next/Library/2019-09-20-14-27-17.bpo-38237.xRUZbx.rst
M Doc/faq/programming.rst
M Doc/library/functions.rst
M Lib/test/test_builtin.py
M Python/bltinmodule.c
M Python/clinic/bltinmodule.c.h

diff --git a/Doc/faq/programming.rst b/Doc/faq/programming.rst
index 2ff7236df1dc..9d45765abaac 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 "<stdin>", line 1, in <module>
-   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 2f3ef4f7fc76..947a0e54aed0 100644
--- a/Doc/library/functions.rst
+++ b/Doc/library/functions.rst
@@ -1272,11 +1272,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`
@@ -1285,14 +1286,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
@@ -1302,6 +1304,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 9baac3cf3b95..cc20551b0dc2 100644
--- a/Lib/test/test_builtin.py
+++ b/Lib/test/test_builtin.py
@@ -19,6 +19,7 @@
 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 @@ def test_pow(self):
 
         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 000000000000..07e1bf712db3
--- /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 c8d34c940f42..0d3f7edb1d90 100644
--- a/Python/bltinmodule.c
+++ b/Python/bltinmodule.c
@@ -1797,22 +1797,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 b936b0cf3721..f6c5b7c1c8bd 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;
-
-    if (!_PyArg_CheckPositional("pow", nargs, 2, 3)) {
+    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;
+
+    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]*/



More information about the Python-checkins mailing list