Python gives you two ways to compare things: check whether two values are equal, or check whether two variables point to the exact same object in memory. The is and is not operators handle the second question. Getting them confused with == is one of the most common sources of subtle bugs in Python code.
What's in this Python Tutorial▼
Every object in Python lives at a specific memory address. Identity operators ask a single yes-or-no question: do these two names refer to the very same object? That is different from asking whether the values they hold happen to look the same. This tutorial covers both operators, shows you exactly when Python reuses objects behind the scenes, and gives you a clear rule for when to reach for is versus ==.
What Are Identity Operators?
Python has two identity operators: is and is not. They test whether two variable names refer to the same object in memory, not whether the objects contain the same value.
Under the hood, a is b is equivalent to id(a) == id(b). The built-in id() function returns the memory address of an object as an integer. If both names map to the same address, they are the same object.
A critical real-world use case is the sentinel pattern. Functions that can return a meaningful None — as opposed to "nothing was found" — often use a private sentinel object as the default. Because the sentinel is a unique object, is is the only reliable way to distinguish "the caller did not pass an argument" from "the caller deliberately passed None". This pattern is used inside the CPython standard library itself.
is checks memory address equality. == checks value equality. Two objects can be equal in value while living at completely different addresses in memory.
Reserve is for comparisons against singleton objects: None, True, and False. The Python style guide (PEP 8) explicitly recommends writing if x is None rather than if x == None.
# Identity operators: is and is not
a = [1, 2, 3]
b = a # b points to the same list object as a
c = [1, 2, 3] # c is a new list with the same values
print(a is b) # True — same object in memory
print(a is c) # False — different objects, same values
print(a == c) # True — values are equal
print(a is not c) # True — confirming they are not the same object
# Checking identity with id()
print(id(a)) # e.g. 140234567890
print(id(b)) # same address as a
print(id(c)) # different address
"Comparisons usingisandis nottest for object identity." — Python Language Reference
Build the correct Python statement to check whether a variable result is None:
if result is None:. The is operator checks object identity — since None is a singleton in Python, using is guarantees you are testing for the exact None object. Using == would work in practice but PEP 8 and Python convention require is for singleton comparisons.
is vs == — What Is the Difference?
The == operator calls an object's __eq__ method and returns True when the values are considered equal. The is operator bypasses __eq__ entirely and compares memory addresses directly. This matters because two objects can easily have equal values while sitting at different addresses.
For numbers and strings, CPython sometimes reuses the same object for efficiency (see the next section on interning), so is may return True even for things you created separately. You should never rely on that behaviour — it is an implementation detail, not a language guarantee.
There is also a correctness argument for preferring is None over == None that goes beyond style. Any class can override __eq__ to return whatever it likes. A custom numeric type, a mock object in a test suite, or a pandas DataFrame can all make x == None return something unexpected. The is operator never calls __eq__ — it compares addresses unconditionally, making it immune to operator overloading and therefore the only safe check for singletons.
- What It Tests
- Object identity — same memory address (
id(a) == id(b)) - When to Use
- Comparisons against singletons:
None,True,False
- What It Tests
- That two names do not refer to the same object
- When to Use
- Checking
if result is not Nonebefore using an optional value
- What It Tests
- Value equality — calls
__eq__on the object - When to Use
- Comparing values: numbers, strings, lists, custom objects
The function below is meant to return True if a value was found in a search. It contains one bug. Click the line you think is wrong, then hit check.
result == None to result is None. PEP 8 requires comparisons to None to use is, not ==, because None is a singleton. Using == can also produce unexpected results with objects that override __eq__ to return True when compared against None.
CPython Interning and Cached Objects
CPython, the standard Python implementation, caches small integers (typically -5 through 256) and short strings that look like identifiers. For these cached values, is may return True even when you created two objects independently. This behaviour is an implementation detail and is not guaranteed across Python versions or alternative implementations such as PyPy or Jython.
There is a deeper subtlety worth understanding. When Python compiles a module, it de-duplicates constant literals within the same code object. Two string literals "hello" on the same line or in the same function may therefore share one object even without the standard interning rules applying. This is called constant folding, and it means that is results can differ between interactive sessions and module-level code. You can also explicitly intern a string using sys.intern(), which forces it into the interpreter's shared string table for the lifetime of the process — a technique occasionally useful for memory optimisation with large volumes of repeated strings, but irrelevant to correctness logic.
Aliasing — two names pointing at the same mutable object — is where identity really matters in practice. If you assign b = a where a is a list, then mutating b also mutates a because they are the same object. This is not a bug caused by is — it is a consequence of Python's object model. Recognising it requires understanding identity, which is exactly what is tests.
a and b reference the same object in memory so a is b is True; c holds the same values in a separate object so a is c is False even though a == c is True.How to Use Python Identity Operators
Follow these steps to apply is and is not correctly in your Python code, avoiding the common mistakes that come from confusing identity with equality.
-
Identify singleton comparisons
Check whether you are comparing a variable against
None,True, orFalse. These are the three singleton objects in Python whereisis the correct operator. Writeif value is None, notif value == None. Using==on singletons bypasses the identity check and can produce incorrect results if an object's__eq__method has been customised. -
Use == for all value comparisons
For numbers, strings, lists, tuples, dicts, and custom objects, use
==. It delegates to the object's__eq__method and returns the semantically correct answer regardless of memory address. The exception is objects that override__eq__in unusual ways — mocks, NumPy arrays, and pandas DataFrames all do this. For those, be explicit about what you are testing and avoid relying on==againstNone. -
Diagnose aliasing bugs with id() and break them with copy
When two names unexpectedly mutate each other, call
id()on each. Matching integers confirm aliasing. To break the alias, usecopy.copy()for a shallow copy (new outer container, shared inner objects) orcopy.deepcopy()for a fully independent clone. In tests,assert a is not bis the idiomatic assertion that confirms a copy was actually made rather than an alias created.
# CPython interning — small integers share the same object
x = 256
y = 256
print(x is y) # True — CPython caches integers in the range -5 to 256
p = 257
q = 257
print(p is q) # False in a standard interactive session (outside the cached range)
# Note: may be True inside a compiled module due to co-object reuse
# String interning
s1 = "hello"
s2 = "hello"
print(s1 is s2) # Typically True — CPython interns short identifier-like strings
# Strings with spaces are not automatically interned
s3 = "hello world"
s4 = "hello world"
print(s3 is s4) # Implementation-defined — do not rely on this
# The safe, always-correct approach for value comparison
print(s3 == s4) # True — always use == for value equality
CPython's interning behaviour is an interpreter optimisation, not a language guarantee. The same code may produce different is results in PyPy, Jython, or across CPython versions. Always use == for value comparisons — rely on is only for None, True, and False.
"Comparisons to singletons like None should always be done with is." — PEP 8, Python Style Guide
Python Learning Summary Points
istests object identity (same memory address);==tests value equality (same content).- Use
isonly for comparisons against the singletonsNone,True, andFalse. - CPython caches small integers and short strings, so
ismay appear to work for values — but this is an implementation detail, not a language guarantee. - Use
id()to inspect memory addresses directly when debugging aliasing or identity issues.
Identity operators are a small but precise tool. Once you understand that is asks "are these the same object?" rather than "do these hold the same value?", the rule becomes simple: use is for None, True, and False, and use == for everything else. Apply that rule consistently and you will avoid one of the more subtle categories of Python bugs.
Frequently Asked Questions
is tests whether two variable names refer to the exact same object in memory (identity). == tests whether two objects hold equal values (equality). Two objects can have equal values while sitting at different memory addresses, in which case == returns True but is returns False.
Use is only when comparing against the singletons None, True, and False. PEP 8, the Python style guide, explicitly recommends if x is None over if x == None. For all other comparisons, use ==.
is not is the negated identity operator. It returns True when two names do not refer to the same object. The most common use is if result is not None, which checks that a variable actually holds a value before using it.
CPython, the standard Python implementation, interns small integers (typically -5 to 256) and short identifier-like strings by reusing the same object. This means is can return True for those values even when you created them independently. This is an optimisation detail, not a language guarantee, and should never be relied on in real code.
Use the built-in id() function. It returns an integer representing the memory address of the object in CPython. a is b is exactly equivalent to id(a) == id(b).