Python Gotcha: Identity vs Equality - When 'is' Fails Unexpectedly

Posted on Tue 17 June 2025 in Technical Solutions

Introduction

In the comparisons gotcha I wrote a few years ago, I briefly touched on is vs ==.

Put simply, is should ONLY be used if you are checking if two references refer to the same object. Remember, is compares object references.

Even more simply, is is not checking value. Let's take a look at a couple examples.

Gotcha

Integers

Python, specifically CPython, caches the values of -5 through 256 (inclusive). This means that these small integer values will always refer to the same object.

Note the phrasing there - "the same object".

Outside of that range, though, the same is not true.

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

In both of the above examples, using a == b would have returned True. The mistake was assuming that is does the same thing. It does not.

Strings

String interning is a method of storing only one copy of each distinct immutable string value. Immutable strings can't be changed. Not every string will be interned though. Let's take a look:

>>> a = "Hello"
>>> b = "Hello"
>>> a is b
True
>>> a = "Hello World"
>>> b = "Hello World"
>>> a is b
False
>>> a = "Hello_World"
>>> b = "Hello_World"
>>> a is b
True

The first and last example interned the strings, showing that a and b refer to the same object. But, the second example - Hello World - didn't get interned, so a and b refer to different objects. Why is this?

The short and simply answer is that any string that has only numbers, letters or underscores will be interned. Since Hello World contains a space, it would not be interned.

The Solution

To a new developer that has seen tutorials that read if a is True or if b is None, a conditional for integers or strings following the same pattern appears to be comparing values. If they test it with small, positive numbers or simple one word strings, the assumption holds up.

But, == is for comparing values! Each of the above examples would return True by changing the statement to a == b.

The few times that is is appropriate are when you are checking True/False or None. Otherwise, the vast majority of the time, you want to use an equality check (==)

The Python PEP8 programming recommendations state:

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

The linters in the Python ecosystem report on the usage of is vs == too. flake8 has E711 - Comparison to None should be 'cond is None:'. ruff has a similar report with it's None comparison check.

I highly recommend a linter for your projects to catch this, and other problems that go against best practices.

Remember, is compares object references, not object equality


- is a father, an engineer and a computer scientist. He is interested in online community building, tinkering with new code and building new applications. He writes about his experiences with each of these.