025. Identity Operators
Identity checks object sameness, equality checks value sameness
025. Identity Operators
RAM Manager — Snapshot Cache with None Sentinel
Aryan’s RAM manager keeps a snapshot cache. When a snapshot has not been taken yet, the slot holds None. After a snapshot is taken, the slot holds the data (which could be 0, [], "", or any real value).
The check is_cached(value) should return True for any value that is not None — including 0 MB, empty lists, and empty strings, which are valid (if unusual) results.
The bug: value != None accidentally passes a linter check but is the wrong idiom. More critically, are_same_snapshot(a, b) uses == to compare two snapshot dicts. Equal dicts are not the same object, so mutations to one will silently affect the other if they ARE the same object — the tool needs an identity check, not an equality check.
The lesson: Use
is None/is not None(not== None). Useiswhen you need to know if two variables are aliases for the same object in memory.
💡 Fun fact: Python caches small integers from -5 to 256 and interns short strings as an optimisation — so a = 42; b = 42; a is b happens to return True. But this is an implementation detail, not a language guarantee. Relying on is for integer comparison works by accident for small numbers and silently breaks for larger ones.
⚠️ Watch out: value != None and value == None both work technically, but PEP 8 explicitly recommends against them — linters like flake8 will flag them. The correct idiom is always value is not None and value is None, because None is a singleton and identity is the right semantic.
🤔 Think about it: x is y implies x == y (identity implies equality), but x == y does NOT imply x is y. Can you construct a Python class where a == b is True but a is b is False?
Learning objectives
- Use ‘is’ and ‘is not’ for identity checks
- Use ‘== None’ vs ‘is None’ correctly
- Understand the difference between identity and equality
Key concepts
- identity operators
- is
- is not
- None
Try it
Concept detail
‘is’ checks object identity (same memory address). ‘==’ checks equality (same value). id(x) returns a unique integer identifier for each object. For None, True, False: always use ‘is’ (they’re singletons — only one instance exists). Small integers (-5 to 256) and interned strings may be cached, making ‘is’ True coincidentally — don’t rely on this. Use ‘is’ only for singletons and explicit identity checks. x is y ⟹ x == y, but x == y does NOT imply x is y.
Solution
def is_cached(value):
return value is not None
def are_same_object(a, b):
return a is bTests
def test_cached_with_value():
assert is_cached(42) == True
def test_cached_empty_string():
assert is_cached("") == True
def test_cached_zero():
assert is_cached(0) == True
def test_not_cached():
assert is_cached(None) == False
def test_same_list_object():
lst = [1, 2, 3]
ref = lst
assert are_same_object(lst, ref) == True
def test_different_list_objects():
a = [1, 2, 3]
b = [1, 2, 3]
assert are_same_object(a, b) == False