LEARN PYTHON | Nested Collections And Comprehensions

Lesson objective

Read nested lists and records in stages, create and filter collections with comprehensions, and recognise shared inner objects in shallow copies.

Learn

03.5 | NESTED COLLECTIONS AND COMPREHENSIONS

01 | A COLLECTION CAN CONTAIN COLLECTIONS

A nested collection contains another collection as an item or value. A list of score rows can represent several players; a list of dictionaries can keep each player’s name beside their scores.

Nesting does not create a new mystery type. The outer object has its usual rules, and the selected inner object has its own rules. Read an access in stages so you know what each bracket selects.

Before coding, define what one outer item represents and what the inner values mean. A structure without that agreement is difficult to interpret consistently.

02 | TWO INDICES SELECT TWO LEVELS

For rows = [[8, 6], [4, 9]], rows[1] selects the second inner list, [4, 9]. rows[1][0] then selects its first value, four.

The first bracket chooses a row by convention; the second chooses a position within that row. Swapping them can retrieve a different value. Python does not impose your row/column meaning automatically.

Inner lists can have different lengths. A valid outer index does not guarantee every inner index is valid. Check each selected level when the data shape may vary.

rows = [[8, 6], [4, 9]]
print(rows[1])
print(rows[1][0])

EXPECTED OUTPUT

[4, 9]
4

03 | KEEP NAMED RECORDS TOGETHER

A dictionary can store a name and a list of scores. A list can hold several such records. players[1]["scores"][0] selects the second player, then their score list, then its first score.

Writing temporary names for these stages often improves readability. The long access is not inherently cleverer than clear steps.

This record structure prevents names and scores being separated accidentally when records are processed or displayed.

players = [{"name": "Maya", "scores": [8, 6]},
           {"name": "Kai", "scores": [4, 9]}]
player = players[1]
scores = player["scores"]
print(player["name"], scores[0])

EXPECTED OUTPUT

Kai 4

04 | TRAVERSE WITH NESTED LOOPS

An outer loop selects one row; an inner loop processes that row’s values. Reset a per-row total inside the outer body before the inner loop. Initialise a grand total before both loops.

For rows [8,6] and [4,9], row totals are fourteen and thirteen, giving grand total twenty-seven. Adding each row total once avoids double-counting.

Different row lengths do not require indexing every row with the same assumed width when direct item iteration fits the task.

rows = [[8, 6], [4, 9]]
grand_total = 0
for row in rows:
    row_total = 0
    for score in row:
        row_total += score
    print("Row total:", row_total)
    grand_total += row_total
print("Grand total:", grand_total)

EXPECTED OUTPUT

Row total: 14
Row total: 13
Grand total: 27

05 | START WITH THE CLEAR APPEND LOOP

To build a list of squares, begin with an empty list, visit numbers and append a calculated value. That ordinary loop exposes the source, transformation and destination.

A list comprehension writes this same construction compactly. [number ** 2 for number in range(1, 5)] means calculate one square for each supplied number and collect the results in a new list.

Read the for clause first to identify where values come from, then the expression to see what each output item becomes. The source range’s stop is still excluded.

squares = []
for number in range(1, 5):
    squares.append(number ** 2)
print(squares)

squares = [number ** 2 for number in range(1, 5)]
print(squares)

EXPECTED OUTPUT

[1, 4, 9, 16]
[1, 4, 9, 16]

06 | FILTER BEFORE INCLUDING AN ITEM

A comprehension can add an if clause to decide which source values qualify. [score for score in scores if score >= 5] keeps scores of five or more.

The expression before for defines the output item; the if after the source defines a filter. Keeping a score is different from producing the Boolean result of its test.

Here [score >= 5 for score in scores] would include a Boolean for every source item, rather than excluding low scores. Identify whether the task needs values, transformed values or condition results.

scores = [2, 5, 8, 4]
kept = [score for score in scores if score >= 5]
checks = [score >= 5 for score in scores]
print(kept)
print(checks)

EXPECTED OUTPUT

[5, 8]
[False, True, True, False]

07 | CLEAN TEXT WITH A TRANSFORMATION

A comprehension can call a familiar method on each source string. [name.strip() for name in names] returns a new list of cleaned text values.

It does not edit the original list’s item bindings or original immutable strings. If you also want to remove blank cleaned names, state that filter explicitly.

Keep the operation readable. A multi-line loop is often better when each item needs complicated error handling or several separate actions.

names = [" Maya ", " Kai ", " "]
cleaned = [name.strip() for name in names]
non_blank = [name.strip() for name in names if name.strip()]
print(cleaned)
print(non_blank)

EXPECTED OUTPUT

['Maya', 'Kai', '']
['Maya', 'Kai']

08 | SET AND DICTIONARY COMPREHENSIONS

Use braces for a set comprehension such as {name.lower() for name in names}. It keeps unique transformed names, with no positional order guarantee.

A dictionary comprehension includes a key:value expression. {number: number ** 2 for number in range(1, 4)} maps each number to its square.

Duplicate produced keys overwrite earlier values rather than creating two identical keys. These are concise tools, not a requirement to replace every readable loop.

squares_by_number = {number: number ** 2 for number in range(1, 4)}
print(squares_by_number)

EXPECTED OUTPUT

{1: 1, 2: 4, 3: 9}

09 | SHALLOW COPIES CAN SHARE INNER ROWS

rows.copy() makes a new outer list but still refers to the original inner row lists. Changing copy_rows[0][0] therefore changes a shared inner row visible through rows.

For this simple list-of-numeric-lists shape, [row.copy() for row in rows] creates new outer and row lists. It is not a universal deep-copy strategy for arbitrarily nested objects.

Also avoid grid = [[0] * 3] * 2 when independent mutable rows are required: the outer repetition reuses one row. A comprehension creating [0] * 3 anew for each row keeps these simple rows independent.

rows = [[8, 6], [4, 9]]
copy_rows = rows.copy()
copy_rows[0][0] = 99
print(rows)

grid = [[0] * 3 for _ in range(2)]
grid[0][0] = 1
print(grid)

EXPECTED OUTPUT

[[99, 6], [4, 9]]
[[1, 0, 0], [0, 0, 0]]

10 | NUMBER ITEMS WITH ENUMERATE

enumerate(values, start=1) supplies pairs of a display number and the next value. Unpack each pair into number, value in the for header.

The start sets the numbering, not the source index used for access. It is helpful when displaying quiz options numbered one onwards while the list itself remains zero-based.

The underscore in a comprehension such as for _ in range(2) is a conventional name indicating that the supplied value is not needed.

options = ["list", "set", "tuple"]
for number, option in enumerate(options, start=1):
    print(f"{number}. {option}")

EXPECTED OUTPUT

1. list
2. set
3. tuple

11 | COMPARE A LOOP AND COMPREHENSION

The demo uses [2, 5, 8, 4]. Choose a transformation or filter, then inspect the source value, the decision and the result built so far. It demonstrates fixed examples rather than evaluating arbitrary source.

    12 | YOUR NESTED DATA CHALLENGE

    Create two player records with a name and three scores each. Display each player’s total, then use a comprehension to build a list of qualifying scores from one selected player.

    Show each access stage for the second player’s final score. Test repeated scores and a score below the filter threshold.

    Extension: compare a shallow outer copy with new copied rows. Explain precisely which object changes, rather than claiming that every copy is either entirely linked or entirely independent.

    PUT YOUR SKILLS TO WORK

    Use the Section 3 challenges to practise these ideas independently. Work in your Python editor, predict results and keep a short test record.

    Section 3 challenges Python course navigation

    MATCH THE PYTHON TERM TO ITS MEANING

    Terminology

    Terminology

    Nested collection

    A collection holding another collection.

    Outer list

    The top-level ordered collection.

    Inner row

    A list selected from a containing list.

    Multi-stage access

    Selecting one level and then another.

    Record list

    A list containing dictionaries of named fields.

    Nested loop

    A loop inside another loop’s body.

    Row total

    An accumulator reset for each row.

    Grand total

    An accumulator retaining contributions across rows.

    Comprehension

    A concise expression constructing a collection.

    Output expression

    Defines the generated item for each qualifying source.

    Source clause

    The for part defining where items come from.

    Filter clause

    An if part deciding which items qualify.

    Transformation

    Producing a changed output value from an item.

    Set comprehension

    Builds unique members using braces.

    Dictionary comprehension

    Builds key:value entries using braces.

    Shared inner object

    One mutable object referred to from more than one outer collection.

    enumerate()

    Supplies numbering/value pairs for iteration.

    Underscore convention

    A name often used when the supplied value is not needed.

    Questions

    Questions

    CHECK YOUR UNDERSTANDING

    Select all correct choices. Each exact set earns one point.

    1. For [[8,6],[4,9]], which are correct?
    2. Which support correct nested totals?
    3. Which describe a comprehension?
    4. Which describe a filter?
    5. Which describe cleaned-name construction?
    6. Which describe shallow nested copies?
    7. Which help independent simple grid rows?
    8. Which describe enumerate?

    EXPLAIN AND COMPARE

    Write your answers before revealing each sample.

    1. Explain rows[1][0] in stages.

    2. Explain per-row and grand-total resets.

    3. Rewrite a square-building loop as a comprehension.

    4. Compare filtering with Boolean output.

    5. Explain a shallow nested copy.

    6. Explain the repeated-row trap.

    7. Describe enumerate with start one.

    8. Suggest when a normal loop is clearer.

    CODE IT | YOUR INDEPENDENT CHALLENGE

    Build independent rows and produce filtered results first with a loop and then with a comprehension.

    Work in your Python editor. Keep a working version, explain one design choice and record at least two tests. Use the Learn tab for worked examples before comparing your result.

    Flashcards

    Flashcards

    Click to flip. Select the ideas you need to revisit.

    0 cards selected for revision.

      Selections are kept while this page is open.

      Workbook

      Workbook

      COMING SOON

      The workbook for Nested Collections And Comprehensions is coming soon. Complete the matching task and written questions in your exercise book.