Python Identity and Membership Operators: is and in
Python's is operator checks whether two names point to the same object, and in checks whether a value is a member of a container. They are not the same as ==, which compares values, or as looping by hand through a collection. This article shows how both operators work, when to use them, and the mistakes that trip people up.
Difficulty: Beginner
Helpful to know first: basic variables, Python values and objects, and how lists, tuples, strings, sets, and dictionaries store data.
How Python Decides Whether Two Names Refer to the Same Object
is answers a very specific question: do these two references point to the exact same object in memory? That is why it is called an identity operator. It does not ask whether two objects have the same value; it asks whether they are the same object.
Here is the smallest useful example:
a = [1, 2, 3]
b = a
print(a is b)
print(a == b)
# Output:
# True
# TrueBoth lines are True because b refers to the same list object as a. The list has one identity, and both names point to it.
If you create a second list with the same contents, the values can still match while the objects are different:
a = [1, 2, 3]
b = [1, 2, 3]
print(a is b)
print(a == b)
# Output:
# False
# TrueThat distinction matters when you care about object identity, not just equal values. Most of the time, you compare values with == and reserve is for special cases such as checking for None.
How Python Checks Whether an Item Is Inside a Container
in asks whether a container holds a value. Python uses the container's own membership rules, so the meaning depends on the type on the right side. For sequences like lists and tuples, Python checks items one by one. For strings, it checks for substrings. For sets and dictionaries, membership is optimized for fast lookups.
These examples show the most common cases:
print(3 in [1, 2, 3])
print("cat" in "education")
print("name" in {"name": "Ava", "age": 12})
# Output:
# True
# False
# TrueFor the dictionary, in checks keys, not values. That surprises many beginners. The expression "Ava" in {"name": "Ava", "age": 12} would be False because "Ava" is a value, not a key.
Working Examples of is and in in Real Python Code
Checking for None the right way
Python programmers use is None because None is a singleton object. There is only one None in a program, so identity comparison is the correct tool.
def find_user(users, name):
for user in users:
if user == name:
return user
return None
result = find_user(["Maya", "Noah"], "Iris")
print(result is None)
# Output:
# TrueIf you wrote == None, the code would often work, but it is less precise and can behave badly with custom classes that define unusual equality rules.
Finding whether a value exists before using it
in often replaces a longer loop. That makes code easier to read and usually faster because Python already knows how to test membership for many built-in containers.
inventory = ["pencil", "notebook", "eraser"]
if "notebook" in inventory:
print("You have a notebook.")
else:
print("Notebook missing.")
# Output:
# You have a notebook.The same logic written by hand would need a loop, a flag, and more room for mistakes. The membership operator does exactly what the code says.
Using membership with dictionaries and sets
Membership is especially useful with dictionaries and sets because these types are designed for fast lookups.
scores = {"Ana": 91, "Omar": 88, "Lea": 95}
tags = {"python", "learning", "operators"}
print("Omar" in scores)
print(95 in scores)
print("python" in tags)
# Output:
# True
# False
# TrueFor scores, the key "Omar" is present, but the value 95 is not a key, so the second line is False. A set contains only values, so membership checks the elements directly.
Testing whether two variables point to the same mutable object
Identity matters when two names may share one mutable object. If one name changes the object, the other name sees that same change because both names point to the same object.
first = [10, 20]
second = first
second.append(30)
print(first)
print(first is second)
# Output:
# [10, 20, 30]
# TrueThis is aliasing, not copying. Both names refer to one list object, so changes through one name show up through the other.
How is and in Behave in Detail
is compares identity, not value. That means it can only be True when both operands are the same object. Python may reuse some small integers or strings in an implementation detail, but you should never depend on that behavior for program logic. The safe rule is simple: use is for singleton checks like None, and use == for value comparison.
in calls a container's membership logic. For a list or tuple, Python checks items one at a time until it finds a match or reaches the end. For a string, it searches for a substring. For a dictionary, it checks keys. For a set, membership is designed to be fast because sets are built for this exact job.
A useful memory aid: is means same object, in means found inside.
There is one more detail beginners often miss: in works with any object that supports membership testing, including custom classes that define the right method. That is why the exact behavior depends on the container type.
Predict the Output
These short examples test the two most common misunderstandings: aliasing with is and dictionary membership with in.
Alias or copy?
What does this print?
items = [1, 2]
other = items
other[0] = 99
print(items)
print(items is other)Show answer
The list is changed through other, so both names show the updated value. They still refer to the same object.
# Output:
# [99, 2]
# Trueother = items does not make a copy. It creates a second reference to the same list.
Dictionary values versus keys
What does this print?
data = {"a": 1, "b": 2}
print(2 in data)
print("b" in data)Show answer
Dictionary membership checks keys, not values. The key "b" is present, but the value 2 is not checked by in.
# Output:
# False
# TrueCommon Mistakes with is and in
Using is to compare values instead of identities
Beginners often write is when they mean ==. The code may appear to work for small examples, but it is not reliable for value comparison.
Problem: This code asks whether two strings have the same value, but is checks whether they are the same object.
name = "Py" + "thon"
if name is "Python":
print("Matched")Fix: Use == when you want equal values.
name = "Py" + "thon"
if name == "Python":
print("Matched")The fixed version works because it compares the contents of the strings.
Checking dictionary values with in and getting the wrong answer
Dictionary membership checks keys only. If you want to test values, you need to look at .values().
Problem: This code expects in to find a dictionary value, but it only checks the keys.
grades = {"Ava": 90, "Ben": 85}
if 90 in grades:
print("Found")Fix: Test the values view when you want values, not keys.
grades = {"Ava": 90, "Ben": 85}
if 90 in grades.values():
print("Found")The corrected version works because grades.values() exposes the dictionary values for membership testing.
Using in on the wrong type
The right side of in must be a container or an object that supports membership testing. If it is not, Python raises a TypeError.
Problem: This code tries to use membership on an integer, which is not iterable.
count = 5
if 1 in count:
print("Found")
# TypeError: argument of type 'int' is not iterableFix: Use in with a list, string, set, tuple, dictionary, or another supported container.
count = [5]
if 1 in count:
print("Found")
else:
print("Not found")The fixed version works because the right side is now a list, so Python can check membership.
When to Use is, and When to Use Something Else
| Use this | When you want to know | Example |
|---|---|---|
| is | whether two names refer to the same object | value is None |
| == | whether two objects have equal values | a == b |
| in | whether a value exists inside a container | needle in haystack |
Use is for singletons such as None, and use == for normal value comparison. Use in when you want Python to search a sequence, string, set, or dictionary keys for you. If you need to check dictionary values, use .values(); if you need keys, the plain dictionary is enough.
A practical rule: if you are asking "same object?" use is. If you are asking "same value?" use ==. If you are asking "contained here?" use in.
Common Interview Questions About is and in
Why is None compared with is instead of ==?
None is a singleton, so identity comparison is the clearest and safest way to check for it. It avoids confusion with classes that customize equality.
Does in check dictionary keys or values?
It checks keys. Use dictionary.values() if you need to search values, or dictionary.items() if you want to search key-value pairs.
Is is faster than ==?
is is usually a very simple pointer comparison, so it can be faster, but speed is not the main reason to use it. The main reason is correctness: identity and equality answer different questions.
Try It Yourself
Write a small program that:
- stores a list of colors,
- checks whether "blue" is in the list,
- creates two names that point to the same list,
- prints whether the two names use is to point to the same object.
Expected output:
True
TrueHint: assign one variable to the other to make an alias, not a copy.
Show solution
colors = ["red", "blue", "green"]
print("blue" in colors)
first_list = colors
second_list = first_list
print(first_list is second_list)Summary
- is checks identity, not value.
- == checks whether two objects have equal values.
- in checks whether a value is inside a container.
- Dictionary membership checks keys, not values.
- Use is None for singleton checks.
- Use == for normal comparisons and in for membership tests.
Next, study Python's equality operators and how they differ from identity operators so you can choose the right comparison every time.