Python “is” vs “==” (Comparing Objects the Right Way)

In Python, the difference between the is statement and the == operator is:

  1. The is statement checks if two objects refer to the same object.
  2. The == operator checks if two objects have the same value.

For example:

>>> a = [1, 2, 3]
>>> b = [1, 2, 3]
>>> a is b
False
>>> a == b
True

The variables a and b are different objects even though they have the same value. Thus comparing the values with the == operator returns True but checking if the variables refer to the same object results in False.

In this guide, you will learn what is the is statement, why it is important, and when you should use it.

Object Identity in Python

In Python, two objects with the same value do not imply the objects would be identical. Similar to how in real-life two persons with the same name do not imply they are the same person.

Whenever you create a Python object, Python stores it in memory behind a specific memory address. Each object gets its own unique address in the memory.

You can check the memory address of any Python object using the built-in id() function. It returns an integer value that represents the memory address of the object.

For example:

>>> a = 1000
>>> id(a)
140053230323952

Now, if you create two objects with the same value, the objects still end up in different memory locations. You can verify this by checking the IDs of the objects.

For example:

>>> n1 = 1000
>>> n2 = 1000
>>> id(n1)
140053229510960
>>> id(n2)
140053229510768
>>> id(n1) == id(n2)
False

Here you can see that the IDs are different.

In Python, there is a built-in mechanism for checking if the IDs of two objects are equal. This is the is statement.

The is Statement in Python

The is statement in Python checks if two objects are identical. In other words, it checks if two objects live in the same memory address, that is, if the objects have the same IDs.

The is statement returns True if the objects have the same ID, otherwise False.

With the is statement, you can replace this piece of code:

id(obj1) == id(obj2)

With:

obj1 is obj2

From the previous chapter’s example, you can replace:

>>> id(n1) == id(n2)
False

With:

>>> n1 is n2
False

Now you already understand what is the difference between the equality operator == and the is statement in Python.

Next, let’s take a look at how variables are actually aliases to objects and how it affects the identity of the variables.

Variables Are Aliases in Python

You can think of a Python variable as a name attached to an object. A Python object can have many variables that refer to the same object. Each variable is thus like an alias for an object under the surface.

Let’s take a look at what assigning to a variable really means in Python and how it relates to the identities of the objects.

Python Object References

Have a look at this piece of code:

>>> print(1000)
1000

When you run it, the Python interpreter

  1. Creates an integer object.
  2. Assigns the value 1000 to it.
  3. Shows the value 1000 in the console.

But after this, there is no way for you to access that integer object anymore. It becomes orphaned. However, you can “store” this object into a variable.

But why is the word “store” in quotes?

In reality, you cannot really store objects into variables in Python. Instead, each variable acts as a reference to the actual memory address where the object lives.

To demonstrate this, let’s create a variable that stores an integer:

>>> num = 1000

This piece of code works such that it:

  1. Creates an integer object.
  2. Assigns the object a value of 1000.
  3. Creates an alias called num that can be used to refer to the new integer object.

So the variable num does not store the integer object. It only points to the memory address of that object.

Here is how it looks:

A Python variable that refers to an object

Now, whenever you access the variable num in your code, Python substitutes it with the int object that represents 1000.

>>> print(num)
1000

Example. Let’s create two list variables such that the second variable is set equal to the first one:

>>> a = [1, 2, 3]
>>> b = a
>>> a
[1, 2, 3]

>>> b
[1, 2, 3]

Then, let’s change the first number of list a to 1000 and check the contents of the lists:

>>> a[0] = 1000
>>> a
[1000, 2, 3]

>>> b
[1000, 2, 3]

Wait a minute! Changing the value of list a also changed the value of list b. Why does this happen?

As you already learned, a variable is a pointer to the memory location where the object actually lives. In the above example, you first create a variable a that points to a list:

>>> a = [1, 2, 3]

Then you create a new variable b that points to variable a:

>>> b = a

As you know, when you call a variable, you receive the object toward which the variable points. So the new variable b becomes an alias to the object referred by a.

In other words, now a and b both point to the same object behind the same memory address. Thus, if you modify the list, both a and b will change.

Python variables that point to a list object

You can verify that the objects point to the same memory address by using the is statement:

>>> a is b
True

Now you understand that Python variables are just references to actual objects.

To support understanding, let’s have a look at another example. This time, instead of dealing with list objects, let’s create an integer object that is referenced by variables a and b.

>>> a = 1000
>>> b = a
Python variables that point to an integer object

Now, let’s change the value of a:

>>> a = 2000

Now, let’s take a look at what variables a and b look like:

>>> a
2000
>>> b
1000

They are different! The variables a and b point to the same memory location so why did b not change when a changed?

The reason why this happens is you are actually not updating the original integer object. Instead, you are creating a completely new integer object that you assign to variable a.

As you remember, the variable assignment:

>>> a = 2000

Tells the Python interpreter to:

  1. Create a new integer object into a new memory address.
  2. Give it a value of 2000.
  3. Allow calling object with the name a.

In other words, assigning 2000 to the variable a makes it point to a new integer object that lives elsewhere in the memory. On the other hand, variable b still points to the object where variable a previously pointed to.

Two Python variables pointing to different objects

You can verify that the variables point to different objects by using the is statement:

>>> a is b
False

By the way, an integer is an immutable object in Python. This example demonstrates it well. There is no way to modify an existing integer object. Instead, you always create a new object to “change” the value of the original one.

Identity Exceptions

At this point, you know that variable assignment in Python creates a reference to an object.

With this in mind, you are not surprised by:

>>> a = 1000
>>> b = 1000

>>> id(a)
140053230323952

>>> id(b)
140053229510992

>> a is b
False

Here the variables a and b refer to different objects in memory.

But what may be surprising is that by repeating this experiment with smaller values the identities are the same:

>>> a = 10
>>> b = 10

>>> id(a)
9789280
>>> id(b)
9789280

>> a is b
True

So why on earth does this happen?

When you run a Python program, the Python interpreter performs some optimizations under the hood. One of the optimizations is it creates objects that represent integers between -5 and 256. This is simply because those integer values are so commonly used.

Now, if you initialize an integer with a value between this range, the Python interpreter reuses a corresponding pre-built integer object instead of creating a new one. So a variable between -5 and 256 always references the same pre-built integer object.

If you create an integer outside of the range [-5, 256], you always create a new integer object.

This leads to inconsistencies when using the is statement over ==:

For example:

>>> a = 100
>>> b = 100
>>> a is b
True

>>> x = 1000
>>> y = 1000
>>> x is y
False

Here a and b refer to the same address in memory due to the optimization described above. On the other hand, the values x and y are not optimized and thus point to different memory addresses.

To take home, never use the is statement to compare two variables whose values should be equal!

When Use “==” And When Use “is”

Most of the time, you should use == when making comparisons in Python.

A basic rule of thumb is to:

  • Use the == to check if two objects have the same value.
  • Use the is statement to check if two variables refer to the same object.

Let’s see some examples.

Equal Value Example

When you compare integers, strings, lists, sets, dictionaries, or other custom mutable objects, use the equality operator ==.

>>> a = [1, 2, 3]
>>> b = [1, 2, 3]
>>> if a == b:
...     print("The list contents are the same")
... 
The list contents are the same

None Example

As a best practice, if you compare something with None, use the is statement. Do not use the equality operator ==.

For example:

>>> a = None
>>> if a is not None:
...     print("Not none")
... else:
...     print("None found")
... 
None found

This is also recommended by the PEP8, the official style guide for Python:

Comparisons to singletons like None should always be done with is or is not, never the equality operators.

PEP8

This is because it is possible to write methods into custom classes that treat == None differently than you would expect.

For instance:

>>> import numpy as np
>>> a = np.zeros(5)
>>> a == None
array([False, False, False, False, False])

>>> a is None
False

As you can see, comparing the array of zeros with None using the equality operator gives you an array of Booleans. However, comparing the array with None using the is statement gives you the expected results.

Class Instance Example

Usually, using the is statement is useful when you want to compare an object with something that should exist only once in the memory.

For example, comparing class instances can be smarter using the is statement. This is because you might want to make sure each class instance is unique in the program.

But why not use the == operator in that case?

Because you can override the behavior of the == operator on custom objects.

For example, let’s say you have a User class in which you can compare users by their name. If the names of two users are the same, the == operator returns True. To do this, you need to override a special method called __eq__() that determines what happens when calling == between two objects.

Here is the code:

class User:
    def __init__(self, name):
        self.name = name
    
    def __eq__(self, otheruser):
        return self.name == otheruser.name

Now you can check if two users have the same name using the equality operator:

user1 = User("Alice")
user2 = User("Alice")

print(user1 == user2)

Output:

True

However, now it is not possible to check if there are two variables that point to the same user object. This is bad because you want to ensure that each user is unique in the program, and that no two users refer to the same object.

To overcome this issue, use is to check if the users are the same:

user1 = User("Alice")
user2 = User("Alice")

print(user1 is user2)

Output:

False

As you can see, even though the names of the users are the same, the is statement realizes these variables refer to different user objects.

To conclude, in this case, using the is statement produces more reliable results than the equality operator ==. Using the is statement you can reliably be sure if there is only one user1 in the program.

Conclusion

Today you learned what is the difference between the is statement and the equality operator == in Python.

To recap, the is statement checks if two objects point to the same object in memory, that is if they have the same IDs.

The equality operator == checks if two objects have the same value. But the equality operator does not care if the objects are actually the same object with the same IDs.

Thanks for reading!

Further Reading

Shallow Copy vs Deep Copy in Python

50 Python Interview Questions