What is the difference between `is` and `==` in Python?
answer
- Two different questions, not two spellings
- Same object, or same value?
- One of them cannot be overridden
- id() versus the __eq__ protocol
- None is a singleton, so is None
basics
~20 sis asks whether two names are bound to one and the same object, the identity test behind id(). == asks whether the objects compare equal, dispatching to eq. Compare values with ==; reserve is for None, True, False and sentinels.
solid answer
~50 s`is` compares **identity**: it is true only when both names refer to the same object, and it is equivalent to `id(a) == id(b)` for objects alive at the same time. No class can override it, so it never runs user code and never raises. `==` compares **value** through the `__eq__` protocol: Python calls `type(a).__eq__(a, b)`, then the reflected `type(b).__eq__(b, a)` if the first returns `NotImplemented`, and only then falls back to identity. That fallback is why two plain objects with no `__eq__` compare equal exactly when they are the same object — which is what makes the operators look interchangeable until a class defines value semantics. In practice: use `==` for values, and `is`/`is not` for `None`, `True`, `False` and module-level sentinels, where you genuinely mean *that one object* and a permissive `__eq__` could otherwise lie to you.
code
python · 20 linesclass Point:
def __init__(self, x, y):
self.x, self.y = x, y
def __eq__(self, other):
return isinstance(other, Point) and (self.x, self.y) == (other.x, other.y)
a = Point(1, 2)
b = Point(1, 2)
print(a == b) # True -> __eq__ compares the fields
print(a is b) # False -> two separate objects
print(a is a, id(a) == id(a))
class Plain:
pass
print(Plain() == Plain()) # False -> default equality falls back to identitygo deeper
Be ready to state the one-liner cleanly: is means same object, == means same value, and None is always tested with is. Expect to be asked for an example where two objects are equal but not identical.
Explain the mechanics: == dispatches to eq, may return NotImplemented, tries the reflected operand, and only then falls back to identity. Know that is cannot be overridden and that != is derived from eq.
Demonstrate judgement about where identity is the correct semantics — sentinels, registries, caches, cycle detection — and show you can debug the identity shortcut containers apply before calling eq, which is how NaN-in-a-list surprises reach production.
Own the convention across a codebase: which types get value equality at all, when identity semantics are the honest choice for an entity, and how you keep an expensive recursive eq off hot paths without weakening the contract.
`is` and `==` answer two different questions, and Python never conflates them for you. ## `is` is identity `a is b` is true when both names are bound to one and the same object: one block of memory, one reference count, one set of attributes. It is the only comparison operator in the language that a class cannot influence — there is no dunder behind it. In CPython it is a raw pointer comparison, so it: - is always O(1), - never raises, - and never runs user code. The builtin `id()` exposes the same notion as a number: `a is b` is equivalent to `id(a) == id(b)` **for objects that are alive at the same moment**. That caveat is real: `id()` is only guaranteed unique among simultaneously living objects, and the interpreter is free to reuse the identity of a temporary that has already been freed, so comparing the ids of two throwaway expressions proves nothing. ## `==` is equality, and it is a protocol For `a == b`: 1. The interpreter calls `type(a).__eq__(a, b)`. 2. If that returns the special `NotImplemented` object, it tries the reflected call `type(b).__eq__(b, a)`. If `type(b)` is a proper subclass of `type(a)` and overrides `__eq__`, the subclass gets the first attempt — that is how a subclass can redefine equality against its base. 3. Only if both sides return `NotImplemented` does Python fall back to comparing identity and answer `False` for distinct objects. Because `__eq__` is ordinary Python code, `==` may be slow, may raise, and may return whatever the author of the class decided — including a non-boolean value. Since Python 3, `!=` is derived from `__eq__` automatically unless a class overrides `__ne__`, so you rarely write both. ## The default case is why the two often look the same A plain class that defines no `__eq__` inherits `object.__eq__`, which returns `NotImplemented` for anything but itself; after the fallback, `a == b` is `True` exactly when `a is b`. So for objects with no value semantics, identity *is* equality, and beginners conclude the operators are interchangeable. They are not: the moment a class defines `__eq__` — or you compare two `list`, `str`, `tuple`, `int` or `datetime` values — equality stops tracking identity, and two objects that are equal are routinely not the same object. ## Reserve `is` for singletons and sentinels `None` is a singleton — there is exactly one `None` object per interpreter — so `x is None` is exact, fast, and cannot be fooled. `x == None` runs `x.__eq__(None)`, and a class is entirely free to return `True` from it; a permissive proxy or a null-object wrapper will then be mistaken for `None`. The same reasoning covers: - `True`, `False`, and any module-level marker created with `object()` or an enum member; - "is this the very object I registered", which shows up in caches, registries, weak-reference bookkeeping and cycle detection: there, sameness rather than sameness-of-value is what you mean. ## Where identity misleads Using `is` to compare numbers or strings is **a bug that hides**, because the interpreter may hand back the same cached object for common small values and for identical literals compiled into one code object, making `is` accidentally agree with `==` in tests and in a REPL and then disagree in production once the values are computed, parsed or read from a file. Treat `is` on a value as a defect regardless of how it behaves when you try it. ## Containers use both, in a specific order Membership tests and sequence comparison apply an identity shortcut before calling `__eq__` — the rule is **"identical or equal"**. That is why: - a not-a-number float, which is never equal to itself, is nevertheless found by `in` inside a list that holds that very object; - and why two lists holding the same NaN object compare equal. Dictionaries and sets do the same thing inside a hash bucket: they compare the stored key with the lookup key by identity first, and only then with `==`. Knowing this explains a whole family of "impossible" results without any special-casing. ## Practical rules - Compare values with `==`; compare against `None`, `True`, `False` and sentinels with `is` and `is not`. - Never chain `x is not None` into `not x is None` — it parses correctly but reads badly. - Do not use `is` to test types; use `isinstance`, or `type(x) is SomeClass` when you deliberately want an exact-type check and no subclasses, which is one of the few legitimate uses of `is` on a non-singleton, because classes themselves are singletons within a running interpreter. - Finally, remember that identity comparison is the cheapest thing in the language and equality can be arbitrarily expensive: for large structures, `==` is a recursive walk, and an identity shortcut you write yourself (`if a is b: return True`) is a legitimate first line in your own `__eq__`.
- Why is `x is None` preferred over `x == None`?`None` is a singleton, so identity is an exact test that cannot be fooled and cannot raise. `x == None` calls `x.__eq__(None)`, and a proxy, a null-object wrapper or any permissive class is free to return `True` from it, so a non-None value can masquerade as `None`. The identity form is also faster, because it runs no user code.
- What does `a == b` actually do when the class defines no `__eq__`?It calls `object.__eq__`, which returns `NotImplemented` for anything other than the same object. Python then tries the reflected call on the right operand, gets `NotImplemented` again, and falls back to comparing identity — so the result is `True` only when `a is b`. That fallback, not a special case, is why default instances behave identically under both operators.
- Can you rely on `id()` being unique for the lifetime of a program?No. `id()` is only guaranteed unique among objects that are alive at the same time. Once an object is freed, the interpreter may hand its identity to a new object, so comparing the ids of two temporaries — for example the results of two separate calls — can report a match that means nothing. Keep both objects referenced if you intend to compare them.
Two identical printed copies of the same contract are equal in content but are not the same sheet of paper; is asks whether you are holding that one sheet, == asks whether the wording matches.
saying these in an interview costs you the question
- Says is is just a faster == for any comparison
- Uses is on strings or numbers because it worked in the REPL
- Claims == compares memory addresses
- Writes x == None instead of x is None
- Believes two objects with equal values must be one object
- Thinks comparing a class without __eq__ raises TypeError