is operator

Updated March 16, 2026 · Keywords
keyword identity comparison objects

The is operator in Python compares object identity — it checks whether two references point to the exact same object in memory. This is different from equality (==), which checks whether two objects have the same value.

Syntax

result = object1 is object2
  • Returns True if both variables reference the same object
  • Returns False if they reference different objects

Identity vs Equality

The Key Difference

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

print(a is b)      # False — different objects, same values
print(a is c)      # True — same object
print(a == b)      # True — same values

Why This Matters

# Using == (equality)
x = [1, 2, 3]
y = [1, 2, 3]
print(x == y)      # True — contents are equal

# Using is (identity)
print(x is y)      # False — they are different objects

Common Use Cases

Comparing with None

The most common use of is is checking for None:

value = None

# Correct way to check for None
if value is None:
    print("No value provided")
# No value provided

value = 42
if value is not None:
    print(f"Value is: {value}")
# Value is: 42

Why Not Use == for None?

# Both work, but is is preferred for None
value = None

print(value == None)   # True
print(value is None)   # True

# The difference matters with custom classes
class Sentinel:
    def __eq__(self, other):
        return True  # Always returns True!

s = Sentinel()
print(s == None)   # True — misleading!
print(s is None)   # False — correct!

Comparing with Singletons

Python has a few singletons that should always be compared with is:

x = None
print(x is None)     # True

x = True
print(x is True)    # True

x = False
print(x is False)   # True

Checking Mutable Default Arguments

A common gotcha:

def append_to(element, to=[]):
    to.append(element)
    return to

# The default list is created once and shared!
result1 = append_to(1)
result2 = append_to(2)

print(result1)   # [1, 2] — shared default!
print(result2)   # [1, 2]
print(result1 is result2)   # True — same object!

The fix uses is:

def append_to(element, to=None):
    if to is None:
        to = []  # Create new list each call
    to.append(element)
    return to

result1 = append_to(1)
result2 = append_to(2)

print(result1)   # [1]
print(result2)   # [2]
print(result1 is result2)   # False

Checking Object Type

Use isinstance() for type checking, not is:

x = "hello"

# Wrong
type(x) is str   # Works but not recommended

# Correct
isinstance(x, str)   # True

Custom Classes with eq

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __eq__(self, other):
        return self.x == other.x and self.y == other.y

p1 = Point(1, 2)
p2 = Point(1, 2)

print(p1 == p2)   # True — __eq__ says they are equal
print(p1 is p2)   # False — different objects

When to Use is

Use CaseOperator
Checking for Noneis None / is not None
Comparing with True/False/NotImplementedis True
Checking if mutable default was providedis not None
Verifying object identity for cachingis
Comparing with Ellipsis (…)is ...

When NOT to Use is

Use CaseOperator
Comparing numbers==
Comparing strings==
Comparing lists/tuples==
Type checkingisinstance()

Common Mistakes

Comparing Integers

# Don't do this — it is an implementation detail
a = 256
b = 256
print(a is b)   # True (for small integers, CPython caches them)

a = 257
b = 257
print(a is b)   # False — not cached!

# Always use == for numbers
print(a == b)   # True

Comparing Strings

# Don't rely on string interning
a = "hello world"
b = "hello world"
print(a is b)   # Might be True or False (implementation-dependent)

# Use == for comparison
print(a == b)   # True

Boolean Confusion

# Don't compare with True/False directly
x = 1

# Works but unpythonic
if x == True:

# Pythonic way
if x:  # Truthy check

# If you must check explicitly
if x is True:  # Only matches actual True, not truthy values

Performance Note

is is slightly faster than == because it only compares memory addresses, not contents. However, the difference is negligible in most cases. Always prefer correctness over micro-optimization:

# Fast but wrong
if value is None:  # Only works for None

# Correct
if value == None:  # Works but misleading

# Pythonic
if value is None:  # Correct and idiomatic

See Also