Identity vs Equality, and Membership
Know exactly when to use `is` versus `==`, and how `in` really works.
Two of the most misunderstood operators in Python are is and ==. They answer different questions:
==asks "are these two values equal?" — it calls the object's__eq__.isasks "are these the exact same object in memory?" — it compares identity (thinkid(x)).
a = [1, 2, 3]
b = [1, 2, 3]
print(a == b) # True — same contents
print(a is b) # False — two different list objects
print(a is a) # True — literally the same objectThe one rule that never fails
Use is only for singletons: None, True, False. Write if x is None, never if x == None. For everything else — numbers, strings, lists — compare with ==.
The small-int trap
CPython caches small integers (-5 to 256) and short strings, so 256 is 256 may be True while 257 is 257 may be False. This is an implementation detail — never rely on it.
Membership with in
in tests membership and works on any container. For a list or str it scans (O(n)); for a set or dict it uses hashing (O(1)). Choosing the right container turns a slow lookup into an instant one.
Example
When to use it
- A sentinel check uses 'is None' to detect missing optional arguments instead of '== None'.
- A membership guard uses 'key in config_dict' to safely check for a key before accessing it.
- An interning test uses 'is' on small integers to explain Python's caching behaviour to a junior developer.
More examples
is vs == for None
Shows why 'is None' is preferred: it cannot be tricked by a class overriding __eq__.
value = None
print(value is None) # True (identity check)
print(value == None) # True (but fragile — __eq__ can be overridden)
class Weird:
def __eq__(self, other): return True
w = Weird()
print(w == None) # True (wrong!)
print(w is None) # False (correct)Membership with 'in'
Uses 'in' on a set and a dict; for sets it is O(1), for dicts it checks keys only.
roles = {'admin', 'editor', 'viewer'}
print('admin' in roles) # True (set: O(1))
print('guest' not in roles) # True
config = {'debug': True, 'port': 8080}
print('port' in config) # True (dict checks keys)Integer interning gotcha
Demonstrates CPython's small-integer cache and why 'is' must never be used for value equality.
a = 256
b = 256
print(a is b) # True (CPython caches -5..256)
c = 257
d = 257
print(c is d) # False in some contexts (not cached)
print(c == d) # True — always use == for value equality
Discussion