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:

Python
a = [1, 2, 3]
b = a

print(a is b)
print(a == b)
# Output:
# True
# True

Both 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:

Python
a = [1, 2, 3]
b = [1, 2, 3]

print(a is b)
print(a == b)
# Output:
# False
# True

That 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:

Python
print(3 in [1, 2, 3])
print("cat" in "education")
print("name" in {"name": "Ava", "age": 12})
# Output:
# True
# False
# True

For 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.

Python
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:
# True

If 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.

Python
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.

Python
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
# True

For 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.

Python
first = [10, 20]
second = first
second.append(30)

print(first)
print(first is second)
# Output:
# [10, 20, 30]
# True

This 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?

Python
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.

Terminal
# Output:
# [99, 2]
# True

other = items does not make a copy. It creates a second reference to the same list.

Dictionary values versus keys

What does this print?

Python
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.

Terminal
# Output:
# False
# True

Common 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.

Python
name = "Py" + "thon"
if name is "Python":
    print("Matched")

Fix: Use == when you want equal values.

Python
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.

Python
grades = {"Ava": 90, "Ben": 85}
if 90 in grades:
    print("Found")

Fix: Test the values view when you want values, not keys.

Python
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.

Python
count = 5
if 1 in count:
    print("Found")
# TypeError: argument of type 'int' is not iterable

Fix: Use in with a list, string, set, tuple, dictionary, or another supported container.

Python
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:

Python
True
True

Hint: assign one variable to the other to make an alias, not a copy.

Show solution
Python
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.