Skip to content

What is the difference between == and is operators

Learn how Python's == (value equality) differs from is (object identity), including when to use each and common pitfalls like integer caching and mutable default arguments.

These two operators test fundamentally different things. Confusing them leads to subtle bugs that may only appear in certain conditions.

Equality with == (Value Comparison)

The == operator checks if two objects have the same value. It calls the object's __eq__ method internally.

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

print(a == b)  # True - same contents

Both lists contain identical elements, so they're equal even though they're separate objects.

Identity with is (Object Comparison)

The is operator checks if two references point to the exact same object in memory. It compares id() values.

a = [1, 2, 3]
b = [1, 2, 3]
c = a  # c references the same object as a

print(a is b)  # False - different objects
print(a is c)  # True - same object

print(id(a), id(b), id(c))

The Integer Interning Trap

Python caches small integers (-5 to 256), making is comparisons appear to work but fail unpredictably:

x = 100
y = 100
print(x is y)  # True (same cached object)

x = 1000
y = 1000
print(x is y)  # False (different objects!)

print(x == y)  # True

Correct Usage: Checking for None

The primary use case for is is checking for None, since None is a singleton:

result = some_function()

if result is None:
    print("No result")

if result is not None:
    print(f"Got: {result}")

This is preferred over == None because:

  • It's faster (no method call)
  • It's explicit about checking identity
  • It avoids issues with objects that override __eq__
class Quirky:
    def __eq__(self, other):
        return True  # Equals everything!

q = Quirky()
print(q == None)   # True (misleading!)
print(q is None)   # False (correct)

Checking Boolean Singletons

Similarly, True and False are singletons:

if flag is True:
    print("Explicitly True")

if flag:
    print("Truthy")

Mutable Default Argument Gotcha

Understanding is helps explain a common Python pitfall:

def append_item(item, items=[]):
    items.append(item)
    return items

a = append_item(1)
b = append_item(2)

print(a)       # [1, 2] - unexpected!
print(b)       # [1, 2]
print(a is b)  # True - same object!

The default list is created once and reused. Fix with:

def append_item(item, items=None):
    if items is None:
        items = []
    items.append(item)
    return items

Quick Reference

Scenario Operator Example
Compare values == if x == 10:
Compare strings == if name == "Alice":
Compare lists/dicts == if data == expected:
Check for None is if result is None:
Check not None is not if result is not None:

Summary

Use == for value comparisons-it's the right choice 99% of the time. Reserve is for identity checks, primarily when comparing against None. Never use is with integers, strings, or other values where Python's caching behavior could cause inconsistent results.

Navigation