Lesson 6 of 916 min2 challenges

Lists and Dictionaries: Holding a Pile of Things

Indexes start at 0, slices stop one short, and dictionaries look things up by name.

Lists: a row of numbered slots

One variable holds one thing. When you need to hold a pile of things, you use a list:

scores = [88, 92, 79]
names = ["Maya", "Ben"]
print(scores)
print(names)

Output:

[88, 92, 79]
['Maya', 'Ben']

Square brackets, items separated by commas. A list keeps its order, which is what makes numbered access possible.

You pull items out by index, and indexes start at 0:

scores = [88, 92, 79]
print(scores[0])
print(scores[2])
print(scores[-1])
print(len(scores))

Output:

88
79
79
3

Starting at 0 feels wrong for about a week and then becomes invisible. A useful way to think about it: the index is not "which item" but "how far from the start." The first item is zero steps from the start.

The consequence is that the last item's index is len - 1. Asking for scores[3] in a three-item list gives:

IndexError: list index out of range

Negative indexes count from the right, so scores[-1] is always the last item regardless of length. That's more readable than scores[len(scores) - 1].

Adding, changing, removing

scores = [88, 92, 79]
scores.append(100)
print(scores)

scores[0] = 90
print(scores)

scores.remove(79)
print(scores)

print(92 in scores)

Output:

[88, 92, 79, 100]
[90, 92, 79, 100]
[90, 92, 100]
True

append adds one item to the end. scores[0] = 90 replaces what's at that position. remove(79) deletes the first item equal to 79 — note it takes a value, not an index, which is a frequent source of confusion. in asks whether a value is present and gives back True or False.

One thing to note: these methods change the list in place and give back nothing. Writing scores = scores.append(1) sets scores to None and throws your data away. Call it on its own line.

Quick summaries

nums = [3, 1, 4, 1, 5]
print(sum(nums))
print(max(nums))
print(min(nums))
print(sorted(nums))
print(nums)

Output:

14
5
1
[1, 1, 3, 4, 5]
[3, 1, 4, 1, 5]

Look at the last two lines. sorted() hands back a new sorted list and leaves the original untouched. (nums.sort() is the other option — it rearranges the original and gives back nothing.) Knowing which one you're using saves you from "why didn't my list change" and its twin, "why did my list change."

Slicing: taking a stretch

nums = [0, 1, 2, 3, 4, 5]
print(nums[1:4])
print(nums[:3])
print(nums[3:])

Output:

[1, 2, 3]
[0, 1, 2]
[3, 4, 5]

The end index is excluded, exactly like range. nums[1:4] gives you positions 1, 2, and 3. Leave the left side blank to mean "from the beginning," leave the right side blank to mean "to the end."

The same syntax works on strings: "2026-03-01"[:7] gives "2026-03", which is a handy way to grab a year and month.

Dictionaries: look things up by name

Lists are right when you have many of the same kind of thing. They're painful when you're describing one thing with several attributes, because you'd have to remember that position 2 is the city. Nobody remembers that.

A dictionary labels each value instead of numbering it:

user = {
    "name": "Maya",
    "age": 18,
    "city": "Portland",
}

print(user["name"])
user["age"] = 19
user["email"] = "maya@example.com"
print(user)

Output:

Maya
{'name': 'Maya', 'age': 19, 'city': 'Portland', 'email': 'maya@example.com'}

A dictionary is a set of key: value pairs. Keys are usually strings. Values can be anything, including other lists and dictionaries.

Assigning to a key that already exists replaces the value. Assigning to a key that doesn't exist creates it. Same syntax for both, which is convenient once you expect it.

Asking for a key that isn't there is an error:

user = {"name": "Maya"}
print(user["phone"])
KeyError: 'phone'

When a missing key is a normal possibility rather than a bug, use get:

user = {"name": "Maya"}
print(user.get("phone"))
print(user.get("phone", "not provided"))

Output:

None
not provided

get gives back None for a missing key, or a default you supply. That second form is the backbone of the grouping pattern coming up in a moment.

Looping over both

scores = [88, 92]
user = {"name": "Maya", "age": 18}
names = ["Maya", "Ben"]

for score in scores:
    print(score)

for key, value in user.items():
    print(f"{key}: {value}")

for i, name in enumerate(names):
    print(f"#{i + 1} is {name}")

Output:

88
92
name: Maya
age: 18
#1 is Maya
#2 is Ben

.items() hands you the key and the value together each round. enumerate() hands you the position and the item together — use it when you need a counter alongside the value, instead of managing an index variable yourself.

Note the i + 1 in that last line. Python counts from 0; people count from 1. Converting at the moment of display, and only there, keeps the two conventions from tangling.

Lists of dictionaries: what real data looks like

students = [
    {"name": "Maya", "score": 88},
    {"name": "Ben", "score": 95},
]

for s in students:
    print(f'{s["name"]} scored {s["score"]}')

Output:

Maya scored 88
Ben scored 95

Note the single quotes around the f-string so the double quotes inside the braces don't end it early.

This shape — a list of dictionaries — is what you get back from almost every web API and database query you'll ever touch. Reading it fluently is most of what it takes to read real code.

The grouping pattern

Counting things per category comes up constantly, and there's a standard way to do it:

bills = [
    {"category": "Food", "amount": 25.5},
    {"category": "Transit", "amount": 8.0},
    {"category": "Food", "amount": 12.0},
]

totals = {}
for b in bills:
    name = b["category"]
    totals[name] = totals.get(name, 0) + b["amount"]

for name in sorted(totals):
    print(f"{name}:{totals[name]}")

Output:

Food:37.5
Transit:8.0

The trick is totals.get(name, 0). The first time a category shows up it isn't in the dictionary yet, and get supplies 0 so the addition works. Without it you'd need an if name in totals check every round. Learn this line — you'll write it many times.

sorted(totals) walks the keys in alphabetical order, which makes the output stable instead of depending on insertion order.

Ten-second check

a = [1, 2, 3]
print(a[1:])

The answer is [2, 3] — start at index 1, run to the end. If you said [2], you were thinking of a[1:2], which stops before index 2.

Ad slot (AdSense not configured; ads appear here in production)

Hands-on

Reading it isn't knowing it. Writing it is.

0/2 passed

Challenges and grading are completely free, and a wrong answer costs you nothing. Stuck? Hit "Ask the AI": it can see the code you are writing.

Level 01Guess the output+10 pts · +15 XP

Which Slice Comes Back

What is the output? ```python a = [1, 2, 3] print(a[1:]) ```

  • Might be multiple choice. Wrong answers cost nothing, so just try.
Level 02Write code+20 pts · +30 XP

Total Up Each Category

You are given a `bills` list. Total the amounts per category, then print one line per category sorted by **category name**, formatted as `Category:Total`. Expected output: ``` Food:37.5 Transit:8.0 ```

Sample input

(none)

Expected output

Food:37.5
Transit:8.0
pythonTab = 4 spaces

Finished? Mark it

Marking it done saves your progress, gives you 20 XP and keeps your streak alive.