Python is Operator — Checking Identity

The Python is keyword tests if the left and right operands refer to the same object—in which case it returns True. It returns False if they are not the same object, even if the two objects are equal. For example, the expression [1, 2, 3] is [1, 2, 3] returns False because although both lists are equal, they are two independent objects in memory.

The following example shows that both lists l1 and l2 are the same when tested for equality l1 == l2, but when tested for identity l1 is l2, they point to another object in memory.

>>> l1 = [1, 2, 3]
>>> l2 = [1, 2, 3]
>>> l1 is l2
False
>>> l1 == l2
True

The difference between the ‘is‘ and ‘==‘ operators is that ‘is‘ compares the identity of two objects whereas ‘==‘ compares the equality of two objects.

🌍 Recommended Tutorial: Python “is” vs “==” Operators

For all built-in Python objects such as strings, lists, dicts, functions holds:

💡 Equality follows from identity, i.e., if x is y, then x==y.

Python is Operator Strings

The is operator on two strings checks if both refer to the same object in memory. As strings are immutable — that is they cannot be modified after creation — the same string is always represented by the same object in memory. Thus, identity is the same as equality for strings, i.e., s1 == s2 is the same as s1 is s2. This is more efficient as holding two equal string objects in memory. If the strings cannot be changed, all variables could simply refer to the same immutable string object.

The following example shows how to independently “created” string variables really refer to the same object in memory:

>>> s1 = 'hi'
>>> s2 = 'hi'
>>> s1 is s2
True

Consequently, equal strings are identical strings and identical strings are equal strings:

>>> s1 == s2
True

Python is Operator Integers

Warning: Do not use the identity operator x is y to compare integers x and y in Python. Instead, use the equality operator x == y. The reason has to do with the internal representation of the identity operator:

# Expected Behavior
>>> 3 is 3
True
>>> 999 is 999
True
>>> 999 is 998 + 1
True

Here’s the problem though: both variables in the following example do not point to the same integer, even though they are equal!

# Unexpected Behavior
>>> x = 257
>>> y = 257
>>> x is y
False
>>> x == y
True

However, the explanation is simple. The is operator only checks if both variables refer to the same object.

In the previous code example, you create two variables x and y and you shouldn’t actually expect those variables to refer to the same object in memory. This is not guaranteed by Python—it’s only an implementation detail (almost by accident) that those two variables may point to the same object if they are immutable.

However, for efficiency reasons, Python’s creators chose to represent integers from -5 to 256 with the same object reference but not the rest of them. Technically, those “smaller” numbers -5 to 256 use the same array data structure, so all variables pointing to those integers actually point to the same array data structure.

A detailed discussion is out of scope here, but you can dive deep into the issue at this excellent StackOverflow thread (this is why I love SO) and on the Finxter blog about Integer Caching.

Python Small Integer Caching

Let’s recap the problem by studying a minimal example again:

a, b = 250, 250
for i in range(250, 260):
    if a is not b:
        break
    a += 1
    b += 1
print(a)
# What's the output of this code snippet?

You’d guess that the for loop goes from i=250 to i=259, each time incrementing a and b. As Python creates one integer object to which both names refer, the command a is not b should always be False. Thus, the result is a=259, right?

WRONG!!! $%&&%$

The result is a=257.

The reason is an implementation detail of the CPython implementation called “Small Integer Caching” — the internal cache of integers in Python.

If you create an integer object that falls into the range of -5 to 256, Python will only return a reference to this object — which is already cached in memory.

“The current implementation keeps an array of integer objects for all integers between -5 and 256, when you create an int in that range you actually just get back a reference to the existing object.”

Python Docs

Again, if you create an integer object that falls into the range of -5 to 256, Python will only return a reference to this object — which is already cached in memory. But if we create an integer object that does not fall into this range, Python may return a new integer object with the same value.

If we now check a is not b, Python will give us the correct result True.

In fact, this leads to the strange behavior of the C implementation of Python 3:

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

Therefore, you should always compare integers by using the == operator in Python. This ensures that Python performs a semantic comparison, and not a mere memory address comparison:

>>> a = 256
>>> b = 256
>>> a == b
True
>>> a = 257
>>> b = 257
>>> a == b
True

What can you learn from this? Implementation details do matter!

Python is Operator Overloading

Python’s operator x is y essentially checks if both objects x and y refer to the same address in memory. A semantically equivalent way would be to compare the integer representations of the object memory addresses using the id() function, i.e., id(x) == id(y). As the memory address is constant throughout an object’s lifetime, you cannot override it. Consequently, you cannot overload the is operator.

The following code indicates that both highlighted ways are semantically identical:

>>> l1 = [1, 2, 3]
>>> l2 = [1, 2, 3]
>>> id(l1)
2860496429120
>>> id(l2)
2860496429376
>>> id(l1) == id(l2)
False
>>> l1 is l2
False

Identity Operators

Python has two identity operators: is and is not. They are used to check if two values or variables reside at the same memory location, i.e., refer to the same object in memory. However, if two variables are equal, this doesn’t imply that they are identical. For example, two lists [1, 2, 3] and [1, 2, 3] may be different objects (not identical) but they’re equal in value.

OperatorMeaningExample
isReturns True if both operands refer to the same object in memory and False otherwise.x is True
is notReturns False if both operands refer to the same object in memory and True otherwise.x is not True

Where to Go From Here?

Enough theory. Let’s get some practice!

Coders get paid six figures and more because they can solve problems more effectively using machine intelligence and automation.

To become more successful in coding, solve more real problems for real people. That’s how you polish the skills you really need in practice. After all, what’s the use of learning theory that nobody ever needs?

You build high-value coding skills by working on practical coding projects!

Do you want to stop learning with toy projects and focus on practical code projects that earn you money and solve real problems for people?

🚀 If your answer is YES!, consider becoming a Python freelance developer! It’s the best way of approaching the task of improving your Python skills—even if you are a complete beginner.

If you just want to learn about the freelancing opportunity, feel free to watch my free webinar “How to Build Your High-Income Skill Python” and learn how I grew my coding business online and how you can, too—from the comfort of your own home.

Join the free webinar now!