• Understanding IS Operator

In this article I want to show you some black coding game magic around operator IS and what kind of jokes it can play with you just because you don't understand how it works.

IS operator tests if result of two expressions represent the same object. For CPython implementation it means that both of those variables points to the same address in memory. (All the examples below are tested on Python 3.6.0)

Now let me show you some magic with something you already know.

>>> a = [[], ] * 3
>>> a
[[], [], []]

In this case we all know that list "a" consist of 3 lists that are not only identical, but also point to the same address in memory.

>>> a[0] is a[1] is a[2]
True
>>> a[0].append(1)
>>> a
[[1], [1], [1]]

Well, this is a pretty much clear example. Now let me show you the kind of magic that happens because of CPython optimisation of immutable objects. Let's take a look on two strings.

>>> a = 'hi'
>>> b = 'hi'

We would expect that "a" and "b" are stored in different parts of memory

>>> a is b
True

but they are not. Does it work the same way for any string?

>>> a ='play coding game CheckiO'
>>> b ='play coding game CheckiO'
>>> a is b
False

Well, it is not. Strings, longer than 20 chars doesn't have this kind of optimisation.

Integers behave in the same manner. Similar magic works for ints between -5 to 257

>>> a = 256
>>> b = 256
>>> a is b
True
>>> a = 257
>>> b = 257
>>> a is b
False

The last magic trick for today will be about mutable Python objects and their optimization. Function ID returns the identity of a given object.

So you can say that the following expression

>>> a is b
True

works the same as

>>> id(a) == id(b)
True

because if two objects have the same id they represent the same object. But let me show you some sorcery.

>>> [] is []
False

>>> id([]) == id([])
True

CPython has few tens of empty lists preconstructed, and can reuse them when they are "garbage collected". In expression id([])==id([]) CPython first evaluates left-hand side, and to do that, it grabs the builtin function id, and goes to evaluate []. It takes one of the preconstructed empty lists, gives it to id, and id returns its address. At that moment, the left [] is not needed anymore, so it is "garbage collected" - in fact, marked as free to be reused. Then CPython evaluates the right-hand side, grabs again the same builtin function id, and goes to evaluate the second []. At that moment, of course, the first empty list is free to be used, and it is reused. Its id is therefore the same.

Conclusion

As you can see in the above examples, understanding of how IS works is very important, because for some cases it looks pretty legit to use it as '==' operator and it can produce some hard to catch bugs.

Of course, I wouldn't be able create such an interesting article full of Python magic by myself. Thank you, veky! We're planning to continue our series of Video Code Review in the near future. So, subscribe on our YouTube channel and stay tuned.

PS: sometimes you send your work on code review to colleagues and wonder whether they really went through it. This can easily be checked by leaving some Easter eggs in code and waiting for your colleagues' reaction. One of this eggs can be replacing "+" on "--0--"

>>> 4 --0-- 5
9

isn't it fun? :)

Welcome to CheckiO - games for coders where you can improve your codings skills.

The main idea behind these games is to give you the opportunity to learn by exchanging experience with the rest of the community. Every day we are trying to find interesting solutions for you to help you become a better coder.

Join the Game