== asks "do these two values match?". is asks "are these the same object in memory?". Most of the time you want the first, and Python quietly caches small numbers in a way that makes the second look right in testing.
The surprise
a = 256 b = int("256") print(a is b) c = 1000 d = int("1000") print(c == d, c is d)
True True False
Same code, different answer. CPython keeps one shared object for every integer from -5 to 256, so 256 is 256 happens to hold. 1000 built at run time is a fresh object: equal in value, but not the same object.
The same thing happens with anything you copy:
orders_today = ["A-1", "A-2"] orders_copy = list(orders_today) print(orders_today == orders_copy, orders_today is orders_copy)
True False
The rule
Use == to compare values. Use is only when identity is the actual question: None, True, False, or a sentinel object you made yourself.
_MISSING = object() def get_price(prices, item, default=_MISSING): if item in prices: return prices[item] if default is _MISSING: raise KeyError(item) return default print(get_price({"tea": 2.5}, "tea")) print(get_price({"tea": 2.5}, "coffee", None))
2.5 None
default is _MISSING can't be fooled: no value a caller passes, not even None, is that exact object.
Why it works
Every object has an identity (its id()), and is compares identities, which is fast and never calls any code. == calls the type's __eq__, which compares contents. Two lists, two strings or two big numbers can be equal without being one object.
When not to use it
Never write x is 1000 or name is "Ada". Python even warns about it (SyntaxWarning: "is" with 'int' literal) because whether it's True depends on caching details that change between versions. And x == None works, but x is None is the convention linters expect and is safe against classes with odd __eq__ methods.