Python’s `is` vs `==` : Why Your Comparisons Sometimes Don’t Work as Expected?

·

·

Python Comparisons

Look at this Python code and try to guess the output:

a = 256
b = 256
c = 1000
d = 1000

print(a is b)   # Line 1
print(c is d)   # Line 2
print([1, 2, 3] == [1, 2, 3])  # Line 3
print([1, 2, 3] is [1, 2, 3])  # Line 4

What do you think the program prints?

  • Will all four lines be True?
  • Maybe True for numbers but False for lists?
  • Or something else entirely?

If you’re not 100% sure, don’t worry — you’re not alone. Many Python developers (even experienced ones) get tripped up by this.

If you can’t confidently explain the output, then you must keep reading. This post will finally make the difference between is and == crystal clear.

Key Concept

At first glance, they seem similar. But as you’ll see, the difference can lead to some very tricky and confuse situations.

  • == (Equality Operator): Compares values.
  • is (Identity Operator): Compares object identity in memory.

Numbers Comparsion

a = 1000
b = 1000

print(a == b)  # True
print(a is b)  # False
  • a == b is True, because both values are 1000.
  • a is b is False, because Python created two different integer objects in memory.

But with smaller numbers, you get a surprise:

x = 100
y = 100

print(x == y)  # True
print(x is y)  # True  (small integers are cached!)
  • Python caches integers between -5 and 256 for performance. That’s why sometimes is returns True for numbers, and sometimes not.

String Comparsion

Strings behave similarly as Numbers.

s1 = "henry"
s2 = "henry"
s3 = "henryc48.sg-host.com"
s4 = "henryc48.sg-host.com"

print(s1 == s2)  # True
print(s1 is s2)  # True (string interning)
print(s3 == s4)  # True
print(s3 is s4)  # False (not interned)

But if you build strings dynamically, Python may not reuse them:

s3 = "hello world!"
s4 = "".join(["hello", " world!"])

print(s3 == s4)  # True
print(s3 is s4)  # False
  • Both look the same, but they’re different objects in memory.
  • Python optimizes small integers and certain strings. So is might accidentally return True. Don’t rely on this.

Lists Comparsion

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

print(list1 == list2)  # True
print(list1 is list2)  # False
  • == says they’re equal because the contents match.
  • is says they’re not identical because they live in different memory locations.
list3 = list1
print(list1 is list3)  # True

Here both names point to the same list. Changing list3 also changes list1.

Immutable objects (numbers, strings, tuples) can be cached and reused. Mutable objects (lists, dicts, sets) are usually new every time.

When to Use Each

Use ==

  • When comparing values (almost always).
  • Example: Checking if two user inputs are the same.
password1 = "secret123"
password2 = "".join(["secret", "123"])

print(password1 == password2)  # True
print(password1 is password2)  # False

Use is

  • When checking for object identity.
  • Especially for None.
value = None

if value is None:  # Best practice
    print("Value is missing")

Let’s Solve the Puzzle

What are the output?

a = 256
b = 256
c = 1000
d = 1000

print(a is b)   # Line 1
print(c is d)   # Line 2
print([1, 2, 3] == [1, 2, 3])  # Line 3
print([1, 2, 3] is [1, 2, 3])  # Line 4
Expand to reveal the output

Line 1 → True: 256 is cached, so a and b point to the same object.

Line 2 → False: 1000 is not cached, so c and d are different objects.

Line 3 → True: The two lists look the same, so == is True.

Line 4 → False: They’re different list objects, so is is False.