← Home

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). Use is when 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 b

Tests

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

Resources