aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAntony Lee <anntzer.lee@gmail.com>2021-09-24 17:22:49 +0200
committerGitHub <noreply@github.com>2021-09-24 16:22:49 +0100
commit6587fc60d447603fb8c631d81d9bb379f53c39ab (patch)
treec52c3172f8845e2cb51eed4338436c9e6f58965a
parentbpo-20524: adds better error message for `.format()` (GH-28310) (diff)
downloadcpython-6587fc60d447603fb8c631d81d9bb379f53c39ab.tar.gz
cpython-6587fc60d447603fb8c631d81d9bb379f53c39ab.tar.bz2
cpython-6587fc60d447603fb8c631d81d9bb379f53c39ab.zip
bpo-44019: Implement operator.call(). (GH-27888)
Having `operator.call(obj, arg)` mean `type(obj).__call__(obj, arg)` is consistent with the other dunder operators. The semantics with `*args, **kwargs` then follow naturally from the single-arg semantics.
-rw-r--r--Doc/library/operator.rst11
-rw-r--r--Doc/whatsnew/3.11.rst8
-rw-r--r--Lib/operator.py7
-rw-r--r--Lib/test/test_operator.py12
-rw-r--r--Misc/NEWS.d/next/Library/2021-08-22-13-25-17.bpo-44019.BN8HDy.rst2
-rw-r--r--Modules/_operator.c22
6 files changed, 62 insertions, 0 deletions
diff --git a/Doc/library/operator.rst b/Doc/library/operator.rst
index 0cdba68f377..146cabc52b0 100644
--- a/Doc/library/operator.rst
+++ b/Doc/library/operator.rst
@@ -250,6 +250,17 @@ Operations which work with sequences (some of them with mappings too) include:
.. versionadded:: 3.4
+
+The following operation works with callables:
+
+.. function:: call(obj, / *args, **kwargs)
+ __call__(obj, /, *args, **kwargs)
+
+ Return ``obj(*args, **kwargs)``.
+
+ .. versionadded:: 3.11
+
+
The :mod:`operator` module also defines tools for generalized attribute and item
lookups. These are useful for making fast field extractors as arguments for
:func:`map`, :func:`sorted`, :meth:`itertools.groupby`, or other functions that
diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst
index 7e041f21c7c..0e56b462f12 100644
--- a/Doc/whatsnew/3.11.rst
+++ b/Doc/whatsnew/3.11.rst
@@ -205,6 +205,14 @@ math
Dickinson in :issue:`44339`.)
+operator
+--------
+
+* A new function ``operator.call`` has been added, such that
+ ``operator.call(obj, *args, **kwargs) == obj(*args, **kwargs)``.
+ (Contributed by Antony Lee in :issue:`44019`.)
+
+
os
--
diff --git a/Lib/operator.py b/Lib/operator.py
index 241fdbb679e..72105be05f1 100644
--- a/Lib/operator.py
+++ b/Lib/operator.py
@@ -221,6 +221,12 @@ def length_hint(obj, default=0):
raise ValueError(msg)
return val
+# Other Operations ************************************************************#
+
+def call(obj, /, *args, **kwargs):
+ """Same as obj(*args, **kwargs)."""
+ return obj(*args, **kwargs)
+
# Generalized Lookup Objects **************************************************#
class attrgetter:
@@ -423,6 +429,7 @@ __not__ = not_
__abs__ = abs
__add__ = add
__and__ = and_
+__call__ = call
__floordiv__ = floordiv
__index__ = index
__inv__ = inv
diff --git a/Lib/test/test_operator.py b/Lib/test/test_operator.py
index b9b8f155826..cf3439fe6fb 100644
--- a/Lib/test/test_operator.py
+++ b/Lib/test/test_operator.py
@@ -518,6 +518,18 @@ class OperatorTestCase:
with self.assertRaises(LookupError):
operator.length_hint(X(LookupError))
+ def test_call(self):
+ operator = self.module
+
+ def func(*args, **kwargs): return args, kwargs
+
+ self.assertEqual(operator.call(func), ((), {}))
+ self.assertEqual(operator.call(func, 0, 1), ((0, 1), {}))
+ self.assertEqual(operator.call(func, a=2, obj=3),
+ ((), {"a": 2, "obj": 3}))
+ self.assertEqual(operator.call(func, 0, 1, a=2, obj=3),
+ ((0, 1), {"a": 2, "obj": 3}))
+
def test_dunder_is_original(self):
operator = self.module
diff --git a/Misc/NEWS.d/next/Library/2021-08-22-13-25-17.bpo-44019.BN8HDy.rst b/Misc/NEWS.d/next/Library/2021-08-22-13-25-17.bpo-44019.BN8HDy.rst
new file mode 100644
index 00000000000..37556d76905
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2021-08-22-13-25-17.bpo-44019.BN8HDy.rst
@@ -0,0 +1,2 @@
+A new function ``operator.call`` has been added, such that
+``operator.call(obj, *args, **kwargs) == obj(*args, **kwargs)``.
diff --git a/Modules/_operator.c b/Modules/_operator.c
index f051513fc79..12a5bf6371b 100644
--- a/Modules/_operator.c
+++ b/Modules/_operator.c
@@ -886,6 +886,27 @@ _operator__compare_digest_impl(PyObject *module, PyObject *a, PyObject *b)
return PyBool_FromLong(rc);
}
+PyDoc_STRVAR(_operator_call__doc__,
+"call($module, obj, /, *args, **kwargs)\n"
+"--\n"
+"\n"
+"Same as obj(*args, **kwargs).");
+
+#define _OPERATOR_CALL_METHODDEF \
+ {"call", (PyCFunction)(void(*)(void))_operator_call, METH_FASTCALL | METH_KEYWORDS, _operator_call__doc__},
+
+static PyObject *
+_operator_call(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
+{
+ if (!_PyArg_CheckPositional("call", nargs, 1, PY_SSIZE_T_MAX)) {
+ return NULL;
+ }
+ return PyObject_Vectorcall(
+ args[0],
+ &args[1], (PyVectorcall_NARGS(nargs) - 1) | PY_VECTORCALL_ARGUMENTS_OFFSET,
+ kwnames);
+}
+
/* operator methods **********************************************************/
static struct PyMethodDef operator_methods[] = {
@@ -942,6 +963,7 @@ static struct PyMethodDef operator_methods[] = {
_OPERATOR_GE_METHODDEF
_OPERATOR__COMPARE_DIGEST_METHODDEF
_OPERATOR_LENGTH_HINT_METHODDEF
+ _OPERATOR_CALL_METHODDEF
{NULL, NULL} /* sentinel */
};