Skip to main content

As we all know, in Python, there are usually multiple ways of getting to the same result. This is the case for compraisons, for instance, since we can use both the == and the is operator.

Let’s see a couple of examples of the interchangeability of these operators.

a = None

print(f'a == None: {a == None}')
print(f'a is None: {a is None}')
a == None: True a is None: True
b = 3
c = 3

print(f'b == c: {b == c}')
print(f'b is c: {b is c}')
b == c: True b is c: True

Therefore, of course, we should get the same for the next example using lists, …right?

list1 = [1, 2, 3]
list2 = [1, 2, 3]

print(f'list1 == list2: {list1 == list2}')
print(f'list1 is list2: {list1 is list2}')
list1 == list2: True list1 is list2: False

Wrong! In this case we are seeing that the is operator is returing False, while the == opeartor keeps on returning True. What is going on here?

The == and is operators

Actually, the == and the is operators are not really the same. The == operator is the equality operator, while the is operator is the identity operator, which are very distinct things.

The equality operator checks if the values stored in the objects that are being compared evaluate to the same. Therefore, in all cases, the result of the == operation has returned True.

However, the identity operator checks, not so surprisingly, for the identity of the object. You can think of it as the position in the memory of your computer that has been assigned to that variable. And, as we can see, it provided a False result when comparing the two equal lists.

But wait! Why does it evaluate to True for the case b is c? We have actually created two different variables, how is it possible that they share the same space in memory (i.e., they are identical)? Well, Python has a special space reserved in memory for some data types (all the possible integers and floats, booleans, the None value…). Therefore, when you create a variable with one of this datatypes, it does not actually create a new space in memory where it stores its value. Instead, this variable will simply point to the specially reserved place in memory.

So, when we do b = 3 and c = 3, we are creating two variables that point to the same space in memory. Therefore, the is operator (which, remember, evaluates to True if and only if the two compared elements are the same object) will evaluate to True.

Instead, when we create, for instance, list or tuple variables, Python reserves a special place in memory for them with the values that they shoud store. Therefore, if we create two lists with the same content, they will be equal but not identical, as we could see before.

The id() function

Just as a final note, you can actually see the ids (kind of the memory address) of any object in Python using the id() function.

b, c, list1, list2 = 3, 3, [1, 2, 3], [1, 2, 3]

print(f'integers: {id(b)} == {id(c)}')
print(f'lists:    {id(list1)} != {id(list2)}')
integers: 140427308353904 == 140427308353904 lists:    140426056029056 != 140426057791104

In fact, you could think of the is operator as doing something like this under the hood.

def alternative_is(object1, object2):
  return id(object1) == id(object2)

Which, of course, behaves as the is operator

print(f'              list1 == list2: {list1 == list2}')
print(f'              list1 is list2: {list1 is list2}')
print(f'alternative_is(list1, list2): {alternative_is(list1, list2)}')
              list1 == list2: True               list1 is list2: False is_alternative(list1, list2): False

I hope that this small post has been interesting and that you have learned something from it. If you are interested in deepening your Python knowledge in more technical aspects I recomend that you check my previous post about mutable and immutable objects.