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 .. 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 The :mod:`operator` module also defines tools for generalized attribute and item
lookups. These are useful for making fast field extractors as arguments for lookups. These are useful for making fast field extractors as arguments for
:func:`map`, :func:`sorted`, :meth:`itertools.groupby`, or other functions that :func:`map`, :func:`sorted`, :meth:`itertools.groupby`, or other functions that

View file

@ -205,6 +205,14 @@ math
Dickinson in :issue:`44339`.) 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 os
-- --

View file

@ -221,6 +221,12 @@ def length_hint(obj, default=0):
raise ValueError(msg) raise ValueError(msg)
return val return val
# Other Operations ************************************************************#
def call(obj, /, *args, **kwargs):
"""Same as obj(*args, **kwargs)."""
return obj(*args, **kwargs)
# Generalized Lookup Objects **************************************************# # Generalized Lookup Objects **************************************************#
class attrgetter: class attrgetter:
@ -423,6 +429,7 @@ __not__ = not_
__abs__ = abs __abs__ = abs
__add__ = add __add__ = add
__and__ = and_ __and__ = and_
__call__ = call
__floordiv__ = floordiv __floordiv__ = floordiv
__index__ = index __index__ = index
__inv__ = inv __inv__ = inv

View file

@ -518,6 +518,18 @@ class OperatorTestCase:
with self.assertRaises(LookupError): with self.assertRaises(LookupError):
operator.length_hint(X(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): def test_dunder_is_original(self):
operator = self.module 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); 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 **********************************************************/ /* operator methods **********************************************************/
static struct PyMethodDef operator_methods[] = { static struct PyMethodDef operator_methods[] = {
@ -942,6 +963,7 @@ static struct PyMethodDef operator_methods[] = {
_OPERATOR_GE_METHODDEF _OPERATOR_GE_METHODDEF
_OPERATOR__COMPARE_DIGEST_METHODDEF _OPERATOR__COMPARE_DIGEST_METHODDEF
_OPERATOR_LENGTH_HINT_METHODDEF _OPERATOR_LENGTH_HINT_METHODDEF
_OPERATOR_CALL_METHODDEF
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
}; };