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.
This commit is contained in:
Antony Lee 2021-09-24 17:22:49 +02:00 committed by GitHub
parent 8d8729146f
commit 6587fc60d4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 62 additions and 0 deletions

View 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

View 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
--

View 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

View 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

View file

@ -0,0 +1,2 @@
A new function ``operator.call`` has been added, such that
``operator.call(obj, *args, **kwargs) == obj(*args, **kwargs)``.

View 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 */
};