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?

+3


source to share


2 answers


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.

+9


source


Here Python source code that implements a comparison ( is

, ==

, <=

etc.):

Python / ceval.c: 4501

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

.

+2


source







All Articles