]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-44019: Implement operator.call(). (GH-27888)
authorAntony Lee <anntzer.lee@gmail.com>
Fri, 24 Sep 2021 15:22:49 +0000 (17:22 +0200)
committerGitHub <noreply@github.com>
Fri, 24 Sep 2021 15:22:49 +0000 (16:22 +0100)
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.

Doc/library/operator.rst
Doc/whatsnew/3.11.rst
Lib/operator.py
Lib/test/test_operator.py
Misc/NEWS.d/next/Library/2021-08-22-13-25-17.bpo-44019.BN8HDy.rst [new file with mode: 0644]
Modules/_operator.c

index 0cdba68f3770edfe9c536021efc1645ecba0eb36..146cabc52b054e10251ec8736979c10a14506943 100644 (file)
@@ -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
index 7e041f21c7cb447b04660ecbb975a8e05039bbcc..0e56b462f1231ffd03f23d1ed430cb1545aef029 100644 (file)
@@ -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
 --
 
index 241fdbb679e7c136808336539cd97a458543662d..72105be05f1c24cabdd292fa4524987b7f54e89d 100644 (file)
@@ -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
index b9b8f15582670834fdb40ee3b9e9ed52b7d50e50..cf3439fe6fb82fab20583ea368060624e13cff55 100644 (file)
@@ -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 (file)
index 0000000..37556d7
--- /dev/null
@@ -0,0 +1,2 @@
+A new function ``operator.call`` has been added, such that
+``operator.call(obj, *args, **kwargs) == obj(*args, **kwargs)``.
index f051513fc793a01b1b5100b038c4bb1fd2c08866..12a5bf6371b459a69157d8c13e6f4cc0a121ebf3 100644 (file)
@@ -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 */
 
 };