Lists: ordered, mutable, indexed by position
A list stores items in insertion order, allows duplicates, and lets you mutate it in place. Index access list[i] is O(1), but membership tests like value in list scan from the start at O(n). On a 100,000-item list, 1,000 membership tests cost 100 million comparisons. Switch to a set for membership and the cost drops to 1,000.
List comprehensions [expr for item in iterable if condition] are the Pythonic way to build a new list from another iterable. They run faster than the equivalent for-loop with .append() because the interpreter optimizes the dedicated bytecode. The pattern shows up in CS50P problem sets weekly: filter a list of student records, square a list of numbers, extract every word longer than 4 characters.
Lists are the right container when order matters, when you need to mutate (append, pop, insert), or when the items repeat. Lists are the wrong container when you only need fast lookup, when you need every item to be unique, or when the data must not change after creation.
# Build a list of squares for x in 0 through 9, keep only the even ones
squares = [x ** 2 for x in range(10) if x % 2 == 0] # list comprehension
print(f'Even squares: {squares}') # [0, 4, 16, 36, 64]
scores = [78, 92, 65, 88, 95, 71] # quiz scores in submission order
scores.append(83) # add one more at the end, O(1)
scores.sort(reverse=True) # in-place sort, descending
print(f'Top 3: {scores[:3]}') # slice the first three
# Membership scan vs set lookup
names = ['Alice', 'Bob', 'Carol', 'Dave'] # small list
if 'Bob' in names: # O(n) for list, fine at this size
print('Bob is enrolled')