By Fatskills Exam Guides Team — the exam nerds behind 28,500+ quizzes and 2.1M practice questions across 500+ global exams.
A hyper-practical, zero-fluff guide for real projects and certifications (PL-300, DP-100, etc.)
You’re building a data pipeline, cleaning a dataset, or optimizing a machine learning feature store. Your code is slow, memory-hungry, or crashes with KeyError/IndexError. The root cause? You picked the wrong Python data structure.
KeyError
IndexError
Real-world scenario:You’re processing 10M user records. A for loop with if x in list takes 30 seconds. Switching to if x in set drops it to 0.003 seconds. That’s a 10,000x speedup—enough to turn an overnight batch job into a real-time API.
for
if x in list
if x in set
What breaks if you ignore this?- Performance: Your ETL pipeline times out in production.- Memory: Your Docker container OOM-kills because you used lists instead of tuples for static data.- Correctness: Your deduplication logic fails silently because you used a list instead of a set.
list
[1, 2, 3]
O(n)
x in list
tuple
(1, 2, 3)
(lat, lon)
set
{1, 2, 3}
x in set
set(list_with_dupes)
set[0]
dict
{"name": "Alice", "age": 30}
dict["key"]
Task: You’re processing a CSV of 1M user records. Your goal: 1. Deduplicate users by email.2. Count unique country values.3. Store results in a dictionary for fast lookups.
email
country
:=
users.csv
email, name, country
❌ Bad (slow, memory-heavy):
import csv emails = [] countries = [] with open("users.csv") as f: reader = csv.DictReader(f) for row in reader: emails.append(row["email"]) # O(1) append, but O(n) lookups later countries.append(row["country"])
✅ Good (optimized):
emails = set() # O(1) membership tests countries = set() with open("users.csv") as f: reader = csv.DictReader(f) for row in reader: emails.add(row["email"]) # Deduplicates automatically countries.add(row["country"])
❌ Bad (O(n²) time):
unique_countries = [] for country in countries: if country not in unique_countries: # O(n) lookup unique_countries.append(country) print(len(unique_countries))
✅ Good (O(1) time):
print(len(countries)) # Sets are already unique
❌ Bad (O(n) lookups):
user_data = [] with open("users.csv") as f: reader = csv.DictReader(f) for row in reader: user_data.append(row) # List of dicts # Later: Find user by email (O(n) time) for user in user_data: if user["email"] == "[email protected]": print(user)
✅ Good (O(1) lookups):
user_dict = {} with open("users.csv") as f: reader = csv.DictReader(f) for row in reader: user_dict[row["email"]] = row # Key: email, Value: full row # Later: Instant lookup print(user_dict["[email protected]"])
import time # Test list vs. set membership big_list = list(range(1_000_000)) big_set = set(big_list) # Time list lookup start = time.time() _ = 999_999 in big_list print(f"List lookup: {time.time() - start:.6f} sec") # ~0.02 sec # Time set lookup start = time.time() _ = 999_999 in big_set print(f"Set lookup: {time.time() - start:.6f} sec") # ~0.000001 sec
Output:
List lookup: 0.021456 sec Set lookup: 0.000001 sec # 20,000x faster
# Good (immutable, memory-efficient) LAT_LON = (37.7749, -122.4194) - Replace `list.count(x)` with `collections.Counter` for frequency counting.python from collections import Counter counts = Counter(countries) # {"US": 1000, "IN": 500, ...} - Use `dict.get(key, default)` to avoid `KeyError` in production.python user = user_dict.get("[email protected]", {"name": "Unknown"}) ```
- Replace `list.count(x)` with `collections.Counter` for frequency counting.
- Use `dict.get(key, default)` to avoid `KeyError` in production.
()
[]
# Good (streams rows one at a time) rows = (row for row in csv.DictReader(f)) - Use `__slots__` in classes to reduce memory overhead (advanced).python class User: slots = ["email", "name"] # Saves ~40% memory vs. dynamic attributes ```
- Use `__slots__` in classes to reduce memory overhead (advanced).
# Good my_list = [x for x in my_list if x % 2 != 0] - Use `frozenset` for immutable sets (e.g., as dictionary keys).python key = frozenset({"US", "IN"}) # Can be a dict key ```
- Use `frozenset` for immutable sets (e.g., as dictionary keys).
for x in my_list[:]
collections.OrderedDict
dict.get(key, default)
try/except
Typical question patterns:1. Performance: "Which data structure is fastest for checking if an element exists in a collection?" - Answer: set (O(1) lookup). - Trap: list (O(n)) or tuple (O(n)).
Trap: list (pre-allocates extra space).
Deduplication: "How do you remove duplicates from a list while preserving order?"
python from collections import OrderedDict list(OrderedDict.fromkeys(my_list))
Trap: set(my_list) (loses order).
set(my_list)
Dictionary keys: "Which of these can be a dictionary key?"
str
int
frozenset
Problem:You have a list of 100,000 user IDs (some duplicates). Write a function that: 1. Returns the count of unique IDs.2. Returns the 10 most frequent IDs.3. Runs in < 0.1 seconds.
Solution:
from collections import Counter def analyze_user_ids(user_ids): unique_count = len(set(user_ids)) # O(n) time, O(n) space top_10 = Counter(user_ids).most_common(10) # O(n log n) time return unique_count, top_10 # Test user_ids = [1, 2, 2, 3, 3, 3] * 100000 unique_count, top_10 = analyze_user_ids(user_ids) print(f"Unique IDs: {unique_count}, Top 10: {top_10}")
Why it works:- set deduplicates in O(n) time.- Counter is optimized for frequency counting (faster than manual loops).
Counter
append
x in l
x in t
x in s
d[key]
x in fs
most_common()
Key defaults:- dict iteration order is insertion-ordered (Python 3.7+).- set and dict resize dynamically (like lists).- tuple is ~25% more memory-efficient than list.
collections
defaultdict
deque
Join 4M+ learners. Unlock unlimited quizzes, wrong-answer tracking, flashcards + reminders, study guides, and 1-on-1 challenges.