Python Up Your Code: Identity vs Equality
Differences between the “==” and the “is” operators in Python

This should be a rather short little article on a topic I think we’re all bound to stumble upon, sooner or later (preferably sooner than later). As simple as this topic may be, taking a deep dive into it can be quite eye-opening. So, let’s get started!
In Python (and some other languages too, but we’re focusing on Python here), there are a certain number of differences between the equality operators like ==, or != and the identity operators like is and is not. Differences that dictate different use cases for each of the two categories. But somehow confusion sets in. Because in one, very particular, yet very common case, these operators actually behave the same.
But let’s exemplify, to get a better understanding of what’s happening.
# 2 equal integers
a = 2
b = 2# test for equality
print(a == b)# test for identity
print(a is b)Output:
True
TrueInteresting, to say the least. Now consider this next example:
# 2 equal lists
l1 = [1, 2, 3]
l2 = [1, 2, 3]# test for equality
print(l1 == l2)# test for identity
print(l1 is l2)Output:
True
FalseThis time around, we could determine that the lists are equal, but the is operator returned False.
Let’s take another look at the first example, but this time, let’s be a little more thorough:
# 2 equal integers
a = 2
b = 2# test for equality
print(a == b)# test for identity
print(a is b)# print out the object ids
print(id(a))
print(id(b))Output:
True
True
140495050653968
140495050653968This example had the == and the is operators both returning True. The equality operator returning True should surprise no one, really. But what’s interesting is the is operator also returning True. Notice how the id() function calls both returned the same object id? Well, now we can get a clear picture of what’s going on.
Turns out, the is operator is testing if the two variables point to the same object in Python’s memory.
- After the first line, where
a = 2, Python created an object with the integer value 2 and assigned it to our variablea; - at the second line, we had
b = 2. So, what happens here is, Python also assigns the variablebto the same objectais referencing, because integers are immutable, so there’s a chance, depending on your Python implementation, that the same integer object will be used (you can read a whole lot more on this delicate topic right here); what we’re looking at is called a shared reference and you can read more about it in here;
Bottom line, these two variables, in this particular case (I’m using the CPython implementation), have ended up referencing the same integer object. This is why a is b returns True. We could also be looking at the a is b line as a shorthand for id(a) == id(b).
And, while we’ve touched on the Python implementation topic, you may have gotten a bit curious about getting your Python implementation. Here’s how:
import platform
platform.python_implementation()Now, back to the task at hand, let’s try the same for our two lists:
# 2 equal lists
l1 = [1, 2, 3]
l2 = [1, 2, 3]# test for equality
print(l1 == l2)# test for identity
print(l1 is l2)# print out the object ids
print(id(l1))
print(id(l2))Output:
True
False
139711984251904
139711982769344Lists are mutable, so even though we created two lists which are equal in value, Python created two separate, distinct objects. The is operator will check for equality among the ids of the two lists and will naturally return False.
It has to be said, same goes for the != vs is not case. It will yield very similar results.
The official Python PEP 8 style guide has a couple of things to say in the way of recommendations on this subject. They make very good points and are definitely worth not just a read, but to actually be put to use, as these can prevent serious bugs from happening:
Comparisons to singletons like None should always be done with
isoris not, never the equality operators.
Also, beware of writing
if xwhen you really meanif x is not None– e.g. when testing whether a variable or argument that defaults to None was set to some other value. The other value might have a type (such as a container) that could be false in a boolean context!
Use
is notoperator rather thannot ... is. While both expressions are functionally identical, the former is more readable and preferred:
# Correct: if foo is not None: # Wrong: if not foo is None:
The first point emphasizes that we should always use is to compare a variable to None instead of ==. Why? Well, I can think of a few reasons:
Noneis a singleton. There can only be oneNoneinstance. There’s no point in comparing the value, of a variable to the value of theNoneobject, since what we really want to do is to test if the variableisNone;- The
==operator can be overridden in some custom classes, by simply overriding the__eq__()method and it can be thatmy_var == Nonemay not return what we may expect it to. Theisoperator, on the other hand, can’t be (so easily) overridden; isis faster, in general, than==. It just checks theid()return values for the operands in question.
The second recommendation is that we write things like if variable and expecting it to work as if variable is not None. Writing shorter lines of code may make it seem a bit cleaner, but it most certainly not correct. Look at it this way: what if our variable was 0? See the problem? None isn’t the only thing that evaluates to False in a boolean context. There’s also False, 0, an empty string and so on. All these can evaluate to False and the if variable will evaluate to False, even though none of them is None.
The third recommendation takes readability very seriously and, granted, when you look at the two lines of code, which one would you choose?
Time to wrap this one up. I hope I managed to somehow shed a bit of light on why knowing your operators can save you from endless hours of hunting weird bugs. We should all strive to become better than our yesterday versions of ourselves. We’ll thank ourselves later. To that end, I’ll see you at the next one! Until then, stay safe and happy coding! Cheers!
Deck is a software engineer, mentor, writer, and sometimes even a teacher. With 12+ years of experience in software engineering, he is now a real advocate of the Python programming language while his passion is helping people sharpen their Python — and programming in general — skills. You can reach Deck on Linkedin, Facebook, Twitter, and Discord: Deck451#6188, as well as follow his writing here on Medium.






