026. Membership Operators
Containment checks for collections and sequences
026. Membership Operators
RAM Manager — Allowlist and Blocklist Checks
Aryan’s RAM manager has two lists:
- Protected processes (never kill):
{"kernel", "systemd", "launchd"} - Known high-memory patterns in process names: substrings like
"leak","zombie"
He writes is_protected(name, protected_set) to check if a name is in the protected set, and has_memory_pattern(cmd_line, patterns) to check if any pattern string appears as a substring inside the full command line.
The bug: is_protected uses name == protected_set (comparing a string to a set, always False) instead of name in protected_set. Protected processes are never recognised, so the tool may kill systemd.
The lesson:
==compares two values for equality.intests membership in a collection or substring presence in a string. They are completely different operations and cannot be swapped.
💡 Fun fact: The in operator works on any iterable in Python, but its performance varies dramatically by collection type: x in list is O(n) (scans every element), while x in set and x in dict are O(1) average (hash lookup). For frequent membership checks on large collections, switching from a list to a set can be the difference between milliseconds and minutes.
⚠️ Watch out: Using == to test membership against a collection (like word == banned_words) always returns False because a string is never equal to a list — the code runs without error but your filter never triggers. Always use word in banned_words for containment.
🤔 Think about it: "free" in "free money" returns True as a substring check. But "free" in ["free money", "click here"] returns False — because it checks for exact list membership, not substrings. How does the type of the container change the meaning of in?
Learning objectives
- Use ‘in’ to check membership in lists, sets, dicts, and substrings in strings
- Use ‘not in’ for negation
- Choose appropriate data structure for efficient membership checks
Key concepts
- membership operators
- in
- not in
- containment
Try it
Concept detail
‘in’ and ‘not in’ are membership operators. They work with:
- Strings: ‘ello’ in ‘Hello’ → True (substring check)
- Lists/tuples: 3 in [1, 2, 3] → True (linear search, O(n))
- Sets: 3 in {1, 2, 3} → True (hash lookup, O(1) average)
- Dicts: ‘key’ in {‘key’: ‘value’} → True (checks keys) ‘not in’ is the negation: 5 not in [1, 2, 3] → True. For performance with frequent lookups, use a set or dict (O(1)) instead of a list (O(n)).
Solution
def is_spam(message, banned_words):
message_lower = message.lower()
for word in banned_words:
if word in message_lower:
return True
return False
def is_premium_user(user_id, premium_ids):
return user_id in premium_idsTests
BANNED = ["buy now", "free money", "click here"]
def test_spam_detected():
assert is_spam("Click here to win!", BANNED) == True
def test_no_spam():
assert is_spam("Hello, how are you?", BANNED) == False
def test_spam_case_insensitive():
assert is_spam("FREE MONEY available!", BANNED) == True
def test_premium_user():
premiums = {101, 202, 303}
assert is_premium_user(202, premiums) == True
def test_not_premium():
premiums = {101, 202, 303}
assert is_premium_user(999, premiums) == False