A study tracker needs more than one number: it needs sessions, topic totals, and perhaps a set of topics already covered. A collection holds related values. Choosing a collection based on the questions you will ask makes the rest of the program simpler. This lesson builds on functions and uses only built-in types plus two standard-library helpers.
Choose a shape for the data
| Collection | Useful question | Important behavior |
|---|---|---|
list | What is the next item in this sequence? | Ordered and mutable; duplicates allowed |
tuple | What values form this fixed record? | Ordered; its item references cannot be replaced |
dict | What value belongs to this key? | Unique keys; remembers insertion order |
set | Have I seen this value before? | Unique members; no defined positional order |
Use a list for a changing sequence and a tuple for a small fixed grouping, such as (topic, minutes). A tuple containing a list still refers to a mutable list: tuple immutability does not freeze everything reachable from it. The data-structures tutorial explains the core operations.
Dictionary keys and set members must be hashable: they need a stable hash and compatible equality behavior. Strings and integers work; ordinary lists and dictionaries do not. An empty dictionary is written with braces, while an empty set requires set().
Transform records in small steps
Save and run this complete example:
sessions = [
{"topic": "loops", "minutes": 20},
{"topic": "files", "minutes": 35},
{"topic": "loops", "minutes": 30},
]
long_sessions = [row for row in sessions if row["minutes"] >= 30]
topics = {row["topic"] for row in sessions}
totals = {}
for row in sessions:
topic = row["topic"]
totals[topic] = totals.get(topic, 0) + row["minutes"]
print([row["minutes"] for row in long_sessions])
print(sorted(topics))
print(totals)
print({topic: minutes * 2 for topic, minutes in totals.items()})
The outputs are [35, 30], ['files', 'loops'], {'loops': 50, 'files': 35}, and {'loops': 100, 'files': 70}. Sorting makes the set display predictable. The dictionary's insertion order is predictable here because loops is inserted first, but insertion order does not mean alphabetical order.
A comprehension builds a collection from an expression and a loop, optionally with a filter. Read the list comprehension as “take each row from sessions, keep qualifying rows.” Use a normal loop when the operation requires several decisions or updates. A dictionary comprehension with duplicate keys keeps the last value for each key; it does not automatically sum records. That is why the example accumulates totals explicitly.
Square-bracket dictionary access requires a key and raises KeyError when it is absent. Use .get() when missing data has a meaningful fallback, as the initial total does here. Avoid adding defaults that hide a malformed required record.
Understand mutation and copies
Assignment does not copy a collection. Both names in other = sessions point to the same list. sessions.copy() copies the outer list, but the dictionaries inside are still shared. This is called a shallow copy.
original = [["loops"]]
shallow = original.copy()
shallow[0].append("files")
print(original)
independent = [row.copy() for row in original]
independent[0].append("tests")
print(original)
print(independent)
The first two outputs are both [['loops', 'files']]; the final output adds tests only to the independent list. Copying each inner list is sufficient for this particular shape. Deeper nesting needs a deliberate copy strategy; blindly copying an entire application object graph can be expensive and misleading.
Useful standard-library collections
collections.Counter counts repeated hashable items. collections.deque supports efficient additions and removals at either end, making it useful for a queue. The collections reference explains when these specialized containers help.
from collections import Counter, deque
counts = Counter(["loops", "files", "loops"])
queue = deque(["read", "practice"])
queue.append("review")
print(counts["loops"])
print(queue.popleft())
print(list(queue))
Expect 2, read, and ['practice', 'review']. A list also supports pop(0), but removing its first element shifts the remaining references.
Pitfalls and practice
items.sort() changes a list and returns None; sorted(items) creates a sorted list. Do not assign the result of .sort() expecting the sorted values. Avoid removing items from a collection while traversing that same collection; produce a filtered result instead.
Exercise: extend the records with a 15-minute files session. Produce topic totals and an alphabetically sorted list of topics with at least 45 minutes.
Solution guidance: reuse the accumulation loop, then filter the totals with a comprehension and sort the topic names. Both topics total 50, so expect ['files', 'loops']. Keep the original session sequence available for later reports.

