What is the difference between 'is' and '==' in Python?
In Python, `is` and `==` are fundamentally different operations that are often confused because they coincidentally produce the same result in common cases.
`==` calls `__eq__()` on the left-hand operand. It tests **value equality** — whether two objects represent the same value according to the class's definition. For built-in types, this is intuitive: `[1, 2] == [1, 2]` is `True` even though they are distinct list objects.
`is` tests **object identity** — whether two names refer to the exact same object in memory. It is equivalent to `id(a) == id(b)`. It does NOT call `__eq__`. No overriding is possible.
The dangerous trap is CPython's optimization of caching small integers (typically -5 to 256) and interned string literals. Because these objects are cached and reused, `is` appears to work:
```python a = 256 b = 256 a is b # True — same cached object
a = 257 b = 257 a is b # False in most contexts — different objects ```
Similarly with strings:
```python 'hello' is 'hello' # True — interned 'hello world' is 'hello world' # May be False ```
This behavior is a CPython implementation detail, not guaranteed by the language specification. Code relying on it is not portable across Python implementations (PyPy, Jython) and can break with dynamic string construction.
**Correct usage of `is`:** Comparing to singletons — `None`, `True`, `False`. PEP 8 explicitly recommends `if x is None` over `if x == None`. This is both more semantically accurate (you mean identity) and safe because `==` can be overridden (`__eq__` might do something unexpected on a custom class).
**When `==` is what you want:** Virtually always, when comparing values — numbers, strings, lists, custom objects. Define `__eq__` on your classes to control equality semantics.
A common bug pattern: checking `if result is True` when a function returns a truthy non-True value (e.g., 1 or a non-empty list). Use `if result == True` or better `if result`.
| Aspect | is | == |
|---|---|---|
| What it tests | Object identity | Value equality |
| Mechanism | id(a) == id(b) | Calls a.__eq__(b) |
| Can be overridden | No | Yes (__eq__) |
| Use with None | Correct (is None) | Risky (__eq__ can lie) |
| CPython small int trap | Yes (appears to work) | Not affected |
| PEP 8 guidance | Only for singletons | Default for value comparison |
Correctly explains identity vs value equality and knows to use 'is None' instead of '== None', with the small int caching caveat.
Covers identity vs value equality, explains id() equivalence, names the CPython caching behavior and why it's unreliable, explains PEP 8's recommendation, and describes __eq__ overriding risk.
Reading the answer is step one. Explaining it unprompted — under interview pressure — is what actually matters. Get AI-graded feedback on your answer with follow-up probes on your weak points.
Get Graded — Free Assessment