Quick summary
Summarize this blog with AI
item in collection asks a membership question and returns a Boolean. What counts as a member depends on the collection: dictionary keys, string substrings and Series index labels are different from a list’s elements. The in in a for statement introduces iteration; it is not itself a Boolean membership test. Examples use Python 3.12.14 and pandas 2.2.3/3.0.6.
Test the intended collection
print(2 in [1, 2, 3], 4 not in (1, 2, 3))
print("sql" in {"python", "sql"})
record = {"status": "paid"}
print("status" in record, "paid" in record, "paid" in record.values())
print("sql" in "sqlpad", "SQL" in "sqlpad", "" in "sqlpad")
print(1 in [True])
Expected output:
True True
True
True False True
True False True
True
Dictionary membership tests keys unless you explicitly select values or items. String membership tests an exact substring and is case-sensitive. An empty string is a substring, which matters in validation. Built-in sequence membership uses identity/equality semantics; True compares equal to 1, so the final result is True. If your domain distinguishes Booleans from integer codes, validate types before membership.
Choose a set only when its semantics fit
allowed = {"paid", "refunded"}
print([status for status in ["paid", "pending", "paid"] if status in allowed])
try:
[1, 2] in {(1, 2)}
except TypeError:
print("set lookup requires a hashable candidate")
Expected output:
['paid', 'paid']
set lookup requires a hashable candidate
A set offers expected constant-time lookup under ordinary hashing assumptions; list membership scans until a match or the end. Building the set costs time and memory, and set membership requires a hashable candidate. The filtered list retains repeated paid inputs and their order. Do not convert the result to a set when duplicates represent real events or output order matters.
Membership can consume an iterator
stream = iter([1, 2, 3, 4])
print(3 in stream)
print(list(stream))
stream = iter([1, 2])
print(9 in stream)
print(list(stream))
Expected output:
True
[4]
False
[]
Finding 3 consumes through that item. An unsuccessful finite search exhausts the iterator. An absent value in an infinite iterator can cause an unbounded search, so do not use membership there without a separate bound. A reusable list does not have this consumption behavior. If you need several passes, choose a bounded reusable representation or redesign the streaming algorithm.
Use Series.isin for value-wise filtering
import pandas as pd
series = pd.Series(["paid", "pending", None], index=[10, 20, 30], dtype=object)
print(10 in series, "paid" in series)
mask = series.isin(["paid", "refunded"])
print(mask.tolist())
print(series.loc[mask].tolist())
print(series.isin([None]).tolist())
Expected output:
True False
[True, False, False]
['paid']
[False, False, True]
in series checks index labels. series.isin(...) produces one Boolean per value. This object-typed fixture retains None explicitly; other dtypes may represent missingness with another sentinel. To select all missing values under pandas semantics, use .isna() instead of guessing which sentinel to include. Passing a single string to Series.isin is an error; pass a list such as ["paid"].
Make text and identity policies visible
Substring membership does not establish a whole-word match, valid email, file extension policy or user identity. Normalize only when the domain permits it, and retain the original value where provenance matters. For the first matching item rather than a Boolean, use the first-match guide. Test an empty collection, a match at the first position, a missing item, a type mismatch and an already-consumed iterator.