DEV Community

Cover image for Why Python's `is` Keyword Will Betray You If You Trust It for Equality
Ameer Abdullah
Ameer Abdullah

Posted on

Why Python's `is` Keyword Will Betray You If You Trust It for Equality

This is one of the most commonly misused Python features and it fails silently, which makes it especially dangerous in production code.

a = "hello"
b = "hello"
print(a == b)
print(a is b)
Enter fullscreen mode Exit fullscreen mode

Both print True. Safe, right?

Now try this:

a = "hello world"
b = "hello world"
print(a == b)
print(a is b)
Enter fullscreen mode Exit fullscreen mode

a == b prints True. a is b might print False.

Same code structure. Different result. Here is exactly why.


What is and == Actually Check

== checks value equality. Do these two objects represent the same value?

is checks identity equality. Are these two names pointing to the exact same object in memory?

Two objects can be equal in value while being completely different objects in memory.


String Interning

Python automatically interns some strings. Interning means Python reuses a single object for multiple names with the same value, rather than creating separate objects.

Short strings that look like valid Python identifiers are automatically interned. Longer strings or strings with spaces are not always interned.

a = "hello"
b = "hello"
print(a is b)  # True -- both point to the same interned object

a = "hello world"
b = "hello world"
print(a is b)  # False -- two separate objects (implementation dependent)
Enter fullscreen mode Exit fullscreen mode

The behavior for longer strings is implementation dependent and version dependent. You cannot rely on it.


Integer Caching

Python caches integers from -5 to 256. Integers in this range are always the same object regardless of how many names point to them.

a = 100
b = 100
print(a is b)  # True -- cached integer, same object

a = 1000
b = 1000
print(a is b)  # False -- outside cache range, separate objects
Enter fullscreen mode Exit fullscreen mode
a = 256
b = 256
print(a is b)  # True -- last cached integer

a = 257
b = 257
print(a is b)  # False -- first uncached integer
Enter fullscreen mode Exit fullscreen mode

The Interview Trap

x = None
y = None
print(x is y)   # True -- None is a singleton
print(x == y)   # True

x = []
y = []
print(x is y)   # False -- two separate empty list objects
print(x == y)   # True -- both represent empty lists
Enter fullscreen mode Exit fullscreen mode

The canonical use case for is is checking against None, True, and False. These are singletons in Python. Every None in your program is the same object.

# Correct pattern
if value is None:
    handle_missing()

# Incorrect pattern (works by accident, not by design)
if value == None:
    handle_missing()
Enter fullscreen mode Exit fullscreen mode

The == version works because NoneType.__eq__ is implemented but using is is the semantically correct choice and is what PEP 8 requires.


The Practical Rule

Use == for value comparison in all application code.
Use is only for checking against None, True, and False.
Never use is to compare strings or integers in application code, even if it works in testing.

Practice identity vs equality problems at PyCodeIt.


Top comments (0)