Python Identity Operators: is and is not
Learn how Python's is and is not operators compare object identity, how they differ from == and !=, and when to use them safely.
Python provides two identity operators: is and is not. They answer a specific question: do two expressions refer to the exact same object? This is different from asking whether two objects contain equal values.
Understanding this difference helps you write reliable checks for None, work safely with mutable objects, and choose the right technique for type validation.
What object identity means
Every Python object has an identity during its lifetime. Object identity is the property that indicates whether two object references point to one exact object.
An object reference is a name or expression that refers to an object. Two references can point to the same object, or they can point to separate objects that happen to contain the same data.
Identity comparisons always produce a Boolean result: True or False.
first = [10, 20]
second = first
print(first is second) # True
print(first is not second) # False
Here, first and second refer to one list object. The assignment does not make a copy.
The is operator
The syntax for the is operator is:
left_operand is right_operand
It returns True when both operands refer to the same object. Otherwise, it returns False.
message = "Ready"
alias = message
print(message is alias) # Usually True here: both names refer to the same object
The important point is not the text stored in the string. The question is whether both names refer to one object.
The is not operator
The syntax for is not is:
left_operand is not right_operand
It returns True when the operands refer to different objects. It is the logical opposite of is.
left = []
right = []
print(left is right) # False
print(left is not right) # True
| Operator | Meaning | Returns True when | Typical use |
|---|---|---|---|
is | Tests object identity | Both operands refer to the same object | Checking for None or a deliberate shared-object relationship |
is not | Tests different identity | The operands refer to different objects | Checking that a value is not None |
Identity versus equality
The equality operators == and != compare values or contents according to the object's equality behavior. The identity operators is and is not compare object references.
list_a = [1, 2, 3]
list_b = [1, 2, 3]
print(list_a == list_b) # True: the contents are equal
print(list_a is list_b) # False: they are separate list objects
Two independently constructed objects can therefore be equal without being identical.
By contrast, aliases refer to the same mutable object:
original = ["red", "blue"]
alias = original
print(original == alias) # True
print(original is alias) # True
alias.append("green")
print(original) # ['red', 'blue', 'green']
A mutable object, such as a list or dictionary, can change after it is created. Since original and alias are aliases for the same list, a change through either name is visible through the other.
| Expression | What it compares | Recommended use | Separate equal lists |
|---|---|---|---|
list_a is list_b | Object identity | Detect whether both names share one list | False |
list_a is not list_b | Different object identity | Detect separate objects | True |
list_a == list_b | Values or contents | Compare list data | True |
list_a != list_b | Unequal values or contents | Test whether data differs | False |
Using is with None
None is Python's singleton object for representing no value, an absent result, or a missing optional value. A singleton is a value represented by one shared object.
Use value is None to test for this object and value is not None to test that a value is present.
def find_user(user_id):
if user_id == 7:
return "Sam"
return None
user = find_user(12)
if user is None:
print("No user was found")
else:
print("User:", user)
Identity comparison is the idiomatic and reliable test for None. Avoid writing user == None. Equality can be customized by user-defined objects, while is None directly expresses the intended singleton identity test.
Identity and type checks
is does not generally determine whether an arbitrary value belongs to a class or type. It compares object identity.
There is one common exact-type pattern:
value = 42
print(type(value) is int) # True
print(type(value) is not float) # True
type(value) returns a type object. In type(value) is int, Python compares that returned type object with the built-in int type object. This is an exact-type check: it accepts only values whose type is precisely int.
When subclasses should also be accepted, use isinstance(). This is the usual choice for type validation.
class SpecialInt(int):
pass
number = SpecialInt(5)
print(type(number) is int) # False: the exact type is SpecialInt
print(isinstance(number, int)) # True: SpecialInt is an int subclass
Use type(value) is BaseClass when an exact type is required. Use isinstance(value, BaseClass) when instances of that class or its subclasses are valid.
Assignment, aliases, and mutable objects
Assignment normally binds another name to an existing object; it does not copy that object.
settings = {"theme": "dark"}
backup_name = settings
print(settings is backup_name) # True
print(settings == backup_name) # True
backup_name["theme"] = "light"
print(settings) # {'theme': 'light'}
backup_name is an alias. If independent dictionaries are needed, create a copy rather than assigning another name to the same dictionary.
settings = {"theme": "dark"}
independent = settings.copy()
print(settings is independent) # False
print(settings == independent) # True
Separate but equal objects
Separate objects are useful when two pieces of data should have the same contents but should be changed independently.
scores_a = {"math": 90, "science": 85}
scores_b = {"math": 90, "science": 85}
print(scores_a == scores_b) # True
print(scores_a is scores_b) # False
Choose the comparison based on the requirement. Use equality when deciding whether the data matches. Use identity when deciding whether two references share one object, such as detecting an alias or a shared sentinel.
Do not rely on incidental identity for immutable values
An immutable object cannot be changed after creation. Integers, strings, and tuples are examples. Python implementations may reuse or cache some immutable objects, including certain small integers or strings.
As a result, an identity check on literals can appear to work in one program or environment and behave differently in another. For example:
number = 1000
# Do not use this to compare numeric values:
# number is 1000
# Use this instead:
print(number == 1000)
Even if is happens to return True for an immutable value in a particular run, that result does not establish a value-comparison rule. Use == for numbers, strings, tuples, and other ordinary value comparisons.
Readable conditional expressions
Write identity checks clearly, especially when combining them with Boolean operators such as and, or, and not.
if result is not None and result != "":
print("A non-empty result is available")
Parentheses can make a more complex condition easier to read:
if (primary is None) or (fallback is None):
print("At least one value is missing")
Avoid overly compact or confusing conditions. Keep the identity test visibly separate from value comparisons.
Choosing a comparison technique
| Goal | Recommended construct | Reason |
|---|---|---|
| Check for no value | value is None | Tests Python's singleton absence value directly |
| Compare ordinary values | left == right | Compares values or contents |
| Require an exact type | type(value) is ExpectedType | Excludes subclasses |
| Accept a type and its subclasses | isinstance(value, ExpectedType) | Performs an inheritance-aware check |
| Determine whether two names share one object | left is right | Tests object identity directly |
Common mistakes and fixes
Two lists print the same, but is is false
This means the lists are distinct objects with equal contents. Use == when the requirement is to compare contents; use is only when shared identity matters.
A numeric identity check appears to work
The runtime may reuse certain immutable objects. Replace comparisons such as number is 0 with number == 0.
A subclass fails an exact type check
type(value) is BaseClass requires the exact type and excludes subclasses. Use isinstance(value, BaseClass) when derived classes should be accepted.
A second list changes when the first list changes
Both names are aliases of the same mutable object. Create an intentional copy when independent containers are required, and use is to verify whether two names share an object.
Summary
- Object identity asks whether two references point to the exact same object.
isreturnsTruefor the same object;is notreturnsTruefor different objects.==and!=compare values or contents, not necessarily object identity.- Use
is Noneandis not Nonefor missing optional values. - Use
type(value) is Typefor an exact-type test andisinstance(value, Type)when subclasses should count. - Do not rely on cached or reused immutable objects to make an identity comparison appear to be a value comparison.
For related foundations, review Python comparison operators, Python logical operators, Python variable data types, and Python lists.