Por que “nenhum” funciona mais rápido do que “== Nenhum”

Em primeiro lugar, devemos saber com certeza que “é Nenhum” funciona mais rápido do que “== Nenhum”:

>>> timeit.timeit("1 is None", number=10000000)
0.4035069934390217
>>> timeit.timeit("1 == None", number=10000000)
0.8190256083633187

Aqui está o código-fonte Python que implementa comparações (é, ==, <=, 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);

Isso é implementado em apenas uma linha de código, uma comparação simples de ponteiro C. Alguns primitivos Python são comparados de acordo com isso (por causa de internação ou porque são singletons como True, False e None).

Por outro lado, eq usa PyObject_RichCompare, que é implementado com a função auxiliar

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);
}

Isso verifica os tipos de argumento e, potencialmente, tenta várias funções de comparação ( métodos eq ) antes de determinar a resposta. Os métodos de comparação podem fazer um trabalho ilimitado (por exemplo, list. Eq tem que verificar cada elemento das listas, possivelmente recursivamente), mas mesmo no caso simples de x == Nenhum, as verificações de tipo e todo o trabalho extra equivalem a um valor real desaceleração em comparação com é .

Com base neste tópico do SO e especialmente nesta resposta