Why is is_ (a, b) function faster than eq (a, b) in python 2.7.3?
I am looking for an answer why it a is None
is faster than a == None
. I am measuring time with this code:
>>> timeit.timeit("1 is None", number=10000000)
0.4035069934390217
>>> timeit.timeit("1 == None", number=10000000)
0.8190256083633187
The documentation says it a is b
has an equialent function is_(a, b)
and that it a == b
has an equivalent function eq(a, b)
. So why is the function is_
faster than eq
?
In some articles I read that it is_()
only compares object ids, but eq()
does a "deep comparison". But I cannot find this information in the documentation. Is this information correct? Where can I find out more about this?
source to share
Testing for Identity ( is
) is a simple pointer comparison (two values of the same object).
Equality testing should do more work than that; for lists, for example, it is necessary to check for equality of every element in both lists until something is checked, negative for equality or all elements in the shortest list.
Note that the two operators are very different from what they are testing:
>>> lsta = []
>>> lstb = lsta
>>> lsta is listb
True
>>> lstc = []
>>> lsta is listc
False
>>> lsta == listc
True
Just because two objects are equal does not mean that they are the same object; is
for the latter.
source to share
Here Python source code that implements a comparison ( is
, ==
, <=
etc.):
static PyObject *
cmp_outcome(int op, register PyObject *v, register PyObject *w)
{
int res = 0;
switch (op) {
case PyCmp_IS:
res = (v == w);
break;
...
default:
return PyObject_RichCompare(v, w, op);
is
implemented in just one line of code, a simple C-pointer comparison. Some Python primitives compare according to this (because of interning or because they are singles of type True
, False
and None
).
On the other hand, it eq
uses PyObject_RichCompare
, which is implemented with a helper function do_richcompare
:
richcmpfunc f;
PyObject *res;
int checked_reverse_op = 0;
if (v->ob_type != w->ob_type &&
PyType_IsSubtype(w->ob_type, v->ob_type) &&
(f = w->ob_type->tp_richcompare) != NULL) {
checked_reverse_op = 1;
res = (*f)(w, v, _Py_SwappedOp[op]);
if (res != Py_NotImplemented)
return res;
Py_DECREF(res);
}
if ((f = v->ob_type->tp_richcompare) != NULL) {
res = (*f)(v, w, op);
if (res != Py_NotImplemented)
return res;
Py_DECREF(res);
}
if (!checked_reverse_op && (f = w->ob_type->tp_richcompare) != NULL) {
res = (*f)(w, v, _Py_SwappedOp[op]);
if (res != Py_NotImplemented)
return res;
Py_DECREF(res);
}
This checks the types of the arguments and potentially tries to use multiple comparison functions (methods __eq__
) before it can figure out the answer. Comparison methods can do unlimited work (for example, list.__eq__
have to check every element of lists, possibly recursively), but even in a simple case, x == None
type checking and all the extra work would mean a real slowdown compared to is
.
source to share