If you look at the following timings:
C:\\Users\\Henry>python -m timeit -s \"mul = int.__mul__\" \"reduce(mul,range(10000))\"
1000 loops, best of 3: 908 u
int.__mul__ is a slot wrapper, namely, a PyWrapperDescrObject, while operator.mul is a buit-in function. I think the opposite execution speed is caused by this difference.
>>> int.__mul__
<slot wrapper '__mul__' of 'int' objects>
>>> operator.mul
<built-in function mul>
When we call a PyWrapperDescrObject, wrapperdescr_call is called.
static PyObject *
wrapperdescr_call(PyWrapperDescrObject *descr, PyObject *args, PyObject *kwds)
{
Py_ssize_t argc;
PyObject *self, *func, *result;
/* Make sure that the first argument is acceptable as 'self' */
assert(PyTuple_Check(args));
argc = PyTuple_GET_SIZE(args);
if (argc d_type->tp_name);
return NULL;
}
self = PyTuple_GET_ITEM(args, 0);
if (!_PyObject_RealIsSubclass((PyObject *)Py_TYPE(self),
(PyObject *)(descr->d_type))) {
PyErr_Format(PyExc_TypeError,
"descriptor '%.200s' "
"requires a '%.100s' object "
"but received a '%.100s'",
descr_name((PyDescrObject *)descr),
descr->d_type->tp_name,
self->ob_type->tp_name);
return NULL;
}
func = PyWrapper_New((PyObject *)descr, self);
if (func == NULL)
return NULL;
args = PyTuple_GetSlice(args, 1, argc);
if (args == NULL) {
Py_DECREF(func);
return NULL;
}
result = PyEval_CallObjectWithKeywords(func, args, kwds);
Py_DECREF(args);
Py_DECREF(func);
return result;
}
Let us look at what we found!
func = PyWrapper_New((PyObject *)descr, self);
A new PyWrapper object has been constructed. It would slow down the execution speed significantly.
Sometimes, it takes more time to create a new object than to run a simple function.
Thus, it is not surprised that int.__mul__
is slower than operator.mul
.