Unravelling `is` and `is not`
As part of this blog series on Python's syntactic sugar, I said in the post on unary arithmetic operators that it might be the most boring post in this series. I think I was wrong. 😄
The operators is and is not are very short. The documentation for the operators says the following:
x is yis true if and only if x and y are the same object.  An Object’s identity is determined using theid()function. Âx is not yyields the inverse truth value.
That's it: call id() on the objects in question and see if you got the same/different values for each of the objects.
The bytecode
Disassembling the two operators leads to the following:
>>> def spam():
... x is y
... x is not y
...
>>> import dis
>>> dis.dis(spam)
2 0 LOAD_GLOBAL 0 (x)
2 LOAD_GLOBAL 1 (y)
4 COMPARE_OP 8 (is)
6 POP_TOP
3 8 LOAD_GLOBAL 0 (x)
10 LOAD_GLOBAL 1 (y)
12 COMPARE_OP 9 (is not)
14 POP_TOP
16 LOAD_CONST 0 (None)
18 RETURN_VALUEis and is notIf you follow into COMPARE_OP you will see it calls cmp_outcome(). The pertinent lines of that function are:
static PyObject *
cmp_outcome(PyThreadState *tstate, int op, PyObject *v, PyObject *w)
{
int res = 0;
switch (op) {
case PyCmp_IS:
res = (v == w);
break;
case PyCmp_IS_NOT:
res = (v != w);
break;cmp_outcome()What that is showing is that is and is not are doing nothing more than comparing the pointers of the objects in question (which are the location in memory for the objects if you are unfamiliar with what a pointer is; PyPy returns a unique number instead).
The code
That translates is and is not to:
def is_(a: Any, b: Any) -> bool:
"""Check if the arguments are the same object."""
return id(a) == id(b)
def is_not(a: Any, b: Any) -> bool:
"""Check if the arguments are different objects."""
return id(a) != id(b)is and is notThis is one of those instances where the tests took up more code than the implementation. 😄
As usual, the source code from this blog post can be found in my desugar project.