is vs == : Identity vs Equality
Overview
The `==` operator compares the VALUES of two objects (equality), while the `is` operator compares the MEMORY ADDRESSES of two objects (identity). Two distinct objects can have the exact same value (`==` is `True`), but because they live in different places in memory, they are not the same object (`is` is `False`). Think of `==` as asking 'Do these two houses look identical?' and `is` as asking 'Are these two addresses pointing to the exact same house?'. In Python, `is` checks if `id(a) == id(b)`. Mixing up `is` and `==` causes subtle bugs when comparing collections. Use `==` when you want to know if the data matches, and use `is` when you need to know if two variables refer to the exact same object in memory. a = [1, 2] b = [1, 2] print(a == b) # True print(a is b) # False (trap!) Comparing mutable structures with `is` fails unexpectedly because Python creates a new object in memory for each list. This behavior is covered fully in `mutable-vs-immutable`. Another sharp edge is integer and string interning. Python caches small integers and short strings for performance, making `is` sometimes appear to work for value comparison. a = 256 b = 256 print(a is b) # True (interned) x = 1000 y = 1000 print(x is y) # False (not interned!) Never use `is` to compare numbers or strings, even if it happens to work for small values. The only time you should consistently use `is` is when checking for singletons like `None`, `True`, or `False`.
Mixing up `is` and `==` causes subtle bugs. Use `==` when you want to know if the data matches. Only use `is` when you need to know if two variables refer to the exact same object in memory (most commonly, checking `x is None`).
Where used: FastAPI, Pydantic, Django
Why learn this
- Preventing subtle bugs when comparing collections
- Checking correctly for singletons like `None`
Code walkthrough
a = [1, 2, 3]
b = [1, 2, 3]
print(a == b)
print(a is b)
Focus: print(a is b)
Aha moment
a = [1, 2, 3]
b = [1, 2, 3]
print(a == b)
x = [1, 2, 3]
y = [1, 2, 3]
print(x is y)
Prediction: What will these two prints output?
Common guess: True, True
Even though `a` and `b` have identical contents (so `a == b` is `True`), lists are mutable objects. Python creates a brand new list in memory every time `[1, 2, 3]` is evaluated. Therefore `x` and `y` point to different memory addresses, and `x is y` is `False`.
Common mistakes
- Using `is` for value comparison: Using `is` instead of `==` for strings or numbers might sometimes work due to Python caching (interning) small integers and short strings, but it will randomly fail for larger values or dynamic inputs. Always use `==` for value comparison.
- Using `==` to check for None: While `x == None` works, the Pythonic and safer way is `x is None`. Some objects (like `pandas` `DataFrame`s or `SQLAlchemy` models) override `__eq__` (which powers `==`) to return an array of booleans or a query expression, which breaks the truth value testing. The `is None` check cannot be overridden.
Glossary
- identity
- The unique memory address or location of an object in a computer, confirming it is the exact same object. Example: `id(a) == id(b)` is the underlying check that `a is b` performs.
- singletons
- Objects or classes that are designed so that only one unique instance of them can ever exist in a program. Example: `None`, `True`, and `False` are singletons in Python — `x is None` is always the right check.
Recall questions
- What is the difference between `==` and `is`?
- When is it appropriate to use the `is` operator?
- Why is it dangerous to use `is` to compare integers or strings for equality?
Understanding checks
What will the following code output?
`True` followed by `False`
`a` and `b` have the same values, so `a == b` is `True`. However, they are two distinct list objects in memory, so `a is b` evaluates to `False`.
Why is it recommended to use `x is None` instead of `x == None`?
Because `is` guarantees an identity check, preventing bugs if an object overrides `==` to behave unexpectedly.
Using `== None` triggers the `__eq__` method, which some libraries override to return custom objects instead of a simple boolean. `is None` directly compares memory addresses and cannot be overridden.
What will the following code output?
`False` followed by `True`
Since `1000` is a large integer, Python does not intern it by default, so `x` and `y` point to different memory addresses (`x is y` is `False`). However, their values are identical, so `x == y` is `True`.
Practice tasks
Fix the singleton comparison
The `process_data` function currently uses `==` to check if the result is `None`. This works most of the time but can be unsafe. Modify the function to use the correct operator for checking identity against singletons like `None`.
Challenge
Safe None Checking
You are writing a database connector. Write a function `process_record(record)` that takes a `record` (which could be a dictionary or `None`). If `record` is exactly `None`, return the string `'No data'`. Otherwise, return the string `'Processing'`. You must use the safest comparison operator for `None`.
Continue learning
Previous: Object identity: id() and memory references
Previous: Primitive types: int, float, bool, str
Next: Mutable vs immutable objects
Next: Shallow vs Deep Copy