Back to Blog
Python

Python Membership Operators: in and not in

python membership operators: Learn how Python's membership operators in and not in work across lists, strings, dictionaries, and sets, including performance and edge c...

membership operatorspython syntaxin operatornot in operatorpython performancepython data structures
Illustration of Python membership operators checking if an item exists in a collection.

Python membership operators in and not in are used to test whether a value exists in a collection. They are fundamental for many tasks, from checking user input against a list of allowed values to verifying the presence of a key in a dictionary. Understanding how these operators behave across different data types and their performance implications is essential for writing efficient Python code.

What Are Python Membership Operators?

The in operator returns True if the left operand is found in the right operand, and False otherwise. The not in operator returns the opposite. The right operand is typically a sequence, container, or any object that supports membership testing.

print("apple" in ["apple", "banana", "cherry"]) # True print("grape" not in ["apple", "banana", "cherry"]) # True

The operators work with many built-in types, including strings, lists, tuples, sets, dictionaries, and ranges. Their behavior, however, differs subtly depending on the type.

How Membership Testing Works on Different Types

Lists and Tuples

For lists and tuples, in performs a linear scan from the first element to the last, comparing each element with the target using equality. This is straightforward and works for any element type.

numbers = [1, 2, 3, 4, 5] print(3 in numbers) # True print(6 in numbers) # False

Strings

For strings, in checks for substring membership, not character-by-character equality. This is a common source of confusion.

text = "hello world" print("world" in text) # True print("hello" in text) # True print("xyz" in text) # False

Membership testing on strings is case-sensitive. To perform a case-insensitive check, you must normalize both sides, for example by converting to lowercase.

Dictionaries

For dictionaries, in checks for the presence of a key, not a value. This is a frequent mistake for developers new to Python.

user = {"name": "Alice", "age": 30} print("name" in user) # True print("Alice" in user) # False print("Alice" in user.values()) # True

To check for a value, you must explicitly use .values() or iterate over items.

Sets

Sets are optimized for membership testing. The in operator uses hashing to achieve average O(1) time complexity, making it much faster than lists for large collections.

allowed_ids = {101, 102, 103} print(102 in allowed_ids) # True print(104 in allowed_ids) # False

Performance Considerations for Membership Testing

The performance of in varies significantly based on the underlying data structure. Lists and tuples require a linear scan, so the time grows proportionally with the number of elements. Sets and dictionaries use hash tables, providing constant-time lookup on average.

Data StructureTime ComplexityUse Case
ListO(n)Small collections or when order matters
TupleO(n)Immutable small collections
SetO(1) averageLarge collections with frequent membership checks
DictionaryO(1) averageKey-based membership checks
StringO(n)Substring search (implementation-dependent)

When you need to repeatedly check membership in a large collection, converting a list to a set once can dramatically reduce runtime. This is a common optimization in data processing and validation code.

# Inefficient for many checks items = ["apple", "banana", "cherry"] for candidate in candidates: if candidate in items: process(candidate) # More efficient: convert to set once items_set = set(items) for candidate in candidates: if candidate in items_set: process(candidate)

However, converting a list to a set also has a cost, so it only pays off when the number of membership checks is large relative to the collection size.

Common Mistakes and Edge Cases

Substring vs. Exact Match

With strings, in checks for substring presence. If you need an exact match, use equality instead:

value = "cat" print("cat" in "concatenate") # True (substring) print(value == "concatenate") # False (exact match)

Dictionary Values

As noted, in on a dictionary checks keys. To check values, you must explicitly use .values(). For large dictionaries, this is O(n) because values are not hashed.

None and Falsey Values

Membership testing works with any object, including None, 0, and empty strings. The operator does not treat falsey values specially.

print(None in [1, None, 2]) # True print(0 in [1, 2, 3]) # False

Custom Objects and Equality

For custom classes, membership testing uses the __eq__ method to compare elements. If you do not define __eq__, Python falls back to identity comparison, which may produce unexpected results.

Using Membership Operators with Custom Classes

You can control how in behaves for your own classes by implementing the __contains__ method. This allows you to define custom membership logic.

class Range: def __init__(self, start, end): self.start = start self.end = end def __contains__(self, item): return self.start <= item <= self.end r = Range(10, 20) print(15 in r) # True print(25 in r) # False

Implementing __contains__ is useful when you want to provide a natural membership test for domain objects, such as checking if a point lies within a polygon or if a user has a certain permission.

Membership Testing in Real-World Code

Membership operators are widely used in validation, filtering, and conditional logic. For example, checking if a request method is allowed:

allowed_methods = {"GET", "POST", "PUT", "DELETE"} if request.method not in allowed_methods: raise HTTPException(405)

Or filtering a list based on membership in another collection:

selected = [item for item in all_items if item.id in allowed_ids]

When using membership operators in performance-critical paths, prefer sets or dictionaries over lists. For one-off checks on small collections, the difference is negligible, but for large data or repeated checks, the choice matters.

Another common pattern is using in to check for the existence of a key before accessing it, though dict.get() or defaultdict can be more concise in some cases. The membership operator is explicit and readable, which is often the best choice for clarity.

python membership operators: Practical Usage and Code Exampl | RYUSLOG DEV