Chapter 7 Quiz: Data Structures

Instructions: Choose the best answer for each multiple-choice question. Written questions ask for a short code snippet or explanation. The answer key is at the end.


Part A: Multiple Choice (Questions 1–12)


Question 1

Which statement correctly describes the difference between a list and a tuple?

A) Lists are faster than tuples for all operations. B) Lists are mutable (can be changed); tuples are immutable (cannot be changed). C) Tuples can hold more items than lists. D) Lists can only contain strings; tuples can contain any type.


Question 2

Priya has this list of weekly sales totals:

weekly_sales = [58320, 62410, 71850, 55200, 68900]

Which expression gives her the last three weeks of sales?

A) weekly_sales[2:5] B) weekly_sales[-3:] C) weekly_sales[3:5] D) Both A and B are correct


Question 3

Sandra has a customer dictionary:

customer = {"name": "Thornfield Logistics", "tier": "gold", "spend": 92100}

She wants to retrieve the "phone" key, but it might not exist. Which is the safest approach?

A) customer["phone"] B) customer.get("phone") C) customer.get("phone", "Not on file") D) "phone" in customer


Question 4

What does the following code print?

products = ["Mouse", "Keyboard", "Monitor", "Webcam"]
products.insert(1, "Hub")
print(products[2])

A) "Keyboard" B) "Hub" C) "Monitor" D) "Mouse"


Question 5

Which data structure would be the most appropriate choice to represent a set of unique active customer IDs?

A) A list, because order matters B) A tuple, because the data shouldn't change C) A set, because only uniqueness matters D) A dictionary, because IDs are strings


Question 6

What is the output of the following code?

regions_a = {"Northeast", "Southeast", "West"}
regions_b = {"West", "Midwest", "Southwest"}
print(regions_a & regions_b)

A) {"Northeast", "Southeast", "West", "Midwest", "Southwest"} B) {"West"} C) {"Northeast", "Southeast"} D) {"Midwest", "Southwest"}


Question 7

Marcus runs this code and is surprised by the result:

original = [{"name": "Thornfield", "tier": "gold"}]
copy_a = original.copy()
copy_a[0]["tier"] = "platinum"
print(original[0]["tier"])

What does it print, and why?

A) "gold" — because copy() creates a fully independent copy. B) "platinum" — because copy() is a shallow copy; the inner dict is still shared. C) None — because copy() empties the original. D) An error is raised.


Question 8

Which list comprehension correctly creates a list of amounts greater than $5,000 from a list of floats?

A) [amount > 5000 for amount in amounts] B) [amount for amount in amounts if amount > 5000] C) [if amount > 5000 then amount for amount in amounts] D) {amount for amount in amounts if amount > 5000}


Question 9

Priya has a list of order tuples: (order_id, region, amount). She wants to build a dict mapping each region to its total amount. Which code is correct?

A)

totals = {}
for order_id, region, amount in orders:
    totals[region] = totals.get(region, 0) + amount

B)

totals = {}
for order in orders:
    totals[order[1]] += order[2]

C)

totals = {region: amount for order_id, region, amount in orders}

D)

totals = dict(orders)

Question 10

What is a named tuple (from collections.namedtuple) most useful for?

A) Creating a dict with default values. B) Representing a fixed record with named fields that shouldn't change. C) Sorting a list by multiple keys. D) Merging two dictionaries.


Question 11

Which statement correctly merges two dictionaries in Python 3.9+, where override values win on conflicts?

A) config = base + override B) config = base | override C) config = merge(base, override) D) config = base.extend(override)


Question 12

Maya has a list of client dicts. She wants to find the client with the highest ytd_revenue_usd. Which is correct?

A) max(clients["ytd_revenue_usd"]) B) clients.max(key="ytd_revenue_usd") C) max(clients, key=lambda c: c["ytd_revenue_usd"]) D) sorted(clients)[0]


Part B: True or False (Questions 13–15)


Question 13

True or False: In Python, dict.items() returns key-value pairs that you can unpack directly in a for loop.


Question 14

True or False: copy.deepcopy() and list.copy() produce the same result when the list contains nested dictionaries.


Question 15

True or False: A Python set can contain duplicate values if those values are added at different times.


Part C: Written Questions (Questions 16–20)


Question 16

Write a single line of code that creates a list of all product names (from a list of product dicts, each with a "name" key) where the product's "in_stock" field is True.

Your list is called products and each item looks like:

{"name": "Webcam", "price": 129.00, "in_stock": True}

Question 17

Below is a partially-completed function. Fill in the missing line:

def revenue_by_category(orders):
    totals = {}
    for order in orders:
        category = order["category"]
        # FILL IN THIS LINE to add order["amount"] to totals[category],
        # initializing to 0 if category not yet in totals
        _______________________________________________
    return totals

Question 18

Priya has this customer database as a list of dicts. Write a function find_customer(database, customer_id) that returns the matching record or None if not found. Write it in 3 lines or fewer (not counting the def line).


Question 19

Explain in 2–3 sentences why copy.deepcopy() is safer than .copy() when working with a list of dictionaries. Give a one-sentence example of when failing to use deepcopy would cause a bug.


Question 20

Given these two sets:

last_quarter_customers = {"Thornfield", "Beacon", "Summit", "Ridgeline"}
this_quarter_customers = {"Thornfield", "Summit", "Apex", "Ironwood"}

Write three lines of code that compute: 1. Customers who are new this quarter (not in last quarter) 2. Customers who churned (were in last quarter but not this quarter) 3. Customers retained across both quarters


Answer Key


Part A: Multiple Choice

Question 1 — Answer: B

Lists are mutable; tuples are immutable. This is the fundamental distinction. Neither is "faster" for all operations (tuples have a slight edge for iteration but this rarely matters in business code). Both can hold any type, and there's no size difference.


Question 2 — Answer: D (Both A and B are correct)

weekly_sales[2:5] starts at index 2 (the third item) and goes to index 4 inclusive — that's items at positions 2, 3, 4 — which are the last three items of a 5-item list. weekly_sales[-3:] starts at three positions from the end. Both produce the same result: [71850, 55200, 68900].


Question 3 — Answer: C

Option A crashes with KeyError if "phone" is absent. Option B returns None without crashing — better, but you'd still need to handle None. Option C returns the default string "Not on file" directly — the cleanest approach. Option D only checks existence (returns True/False) but doesn't retrieve the value.


Question 4 — Answer: A

insert(1, "Hub") inserts "Hub" at index 1, pushing everything else right. After insertion the list is: ["Mouse", "Hub", "Keyboard", "Monitor", "Webcam"]. Index 2 is now "Keyboard".


Question 5 — Answer: C

A set is the right choice for unique active customer IDs. You only care whether an ID is present, not its order or position. Sets automatically enforce uniqueness and provide O(1) membership testing, which is far faster than scanning a list.


Question 6 — Answer: B

& is the intersection operator for sets. It returns only elements that appear in both sets. The only shared element is "West".


Question 7 — Answer: B

list.copy() creates a shallow copy — a new list object, but the inner dictionaries are still the same objects in memory. Modifying copy_a[0]["tier"] modifies the same dict that original[0] also points to. It prints "platinum". The fix is copy.deepcopy(original).


Question 8 — Answer: B

The correct list comprehension syntax is [expression for item in iterable if condition]. Answer A produces a list of booleans. Answer C is syntactically invalid. Answer D uses set comprehension syntax (curly braces), which would produce a set, not a list.


Question 9 — Answer: A

Option A is correct: it uses .get(region, 0) to safely initialize a new key, then adds the amount. Option B would raise KeyError on the first new region (can't += if the key doesn't exist yet). Option C would overwrite each region's total with just the last order's amount, not accumulate them. Option D doesn't work — dict() from a list of 3-tuples is invalid.


Question 10 — Answer: B

Named tuples provide named field access on an immutable record. They are ideal for representing rows of data — like a database query result or a CSV row — where each position has a clear name. They're lighter than a full class and more readable than a plain tuple.


Question 11 — Answer: B

Python 3.9 introduced the | merge operator for dicts. base | override returns a new dict with base values, updated by override values (override wins on key conflicts). Option A is invalid (dicts don't support +). Option C doesn't exist. Option D is a list method, not a dict method.


Question 12 — Answer: C

max(iterable, key=function) is the correct form. The key function must accept a single element and return the comparison value. Answer A tries to index a list as if it were a dict. Answer B is not valid Python syntax. Answer D sorts but doesn't specify any order — and sorted on dicts without a key doesn't sort by a field.


Part B: True or False

Question 13 — Answer: True

dict.items() returns an iterable of (key, value) tuples. In a for loop, you can unpack them directly: for key, value in my_dict.items():. This is one of the most common Python idioms.


Question 14 — Answer: False

list.copy() creates a shallow copy — the outer list is new, but nested objects (like dicts) are still shared references. copy.deepcopy() creates a fully independent copy of the entire nested structure. They produce the same result only for flat lists of immutable values (ints, strings, tuples).


Question 15 — Answer: False

Sets cannot contain duplicates — ever. If you add() a value that already exists in the set, the set is unchanged. That is the defining property of a set.


Part C: Written Questions

Question 16 — Sample Answer:

in_stock_names = [p["name"] for p in products if p["in_stock"]]

This is a list comprehension that extracts the "name" field from each product dict, filtered to only those where "in_stock" is True.


Question 17 — Answer:

totals[category] = totals.get(category, 0) + order["amount"]

totals.get(category, 0) returns the current total for that category (defaulting to 0 if the category hasn't been seen yet), then adds order["amount"] to it, and stores the result back under totals[category]. This is the standard dict aggregation pattern.


Question 18 — Sample Answer:

def find_customer(database, customer_id):
    """Return the customer record with the given ID, or None."""
    for record in database:
        if record["customer_id"] == customer_id:
            return record
    return None

This can also be written more compactly using next():

def find_customer(database, customer_id):
    return next((r for r in database if r["customer_id"] == customer_id), None)

Both approaches are correct. The next() version is more Pythonic; the loop version is more readable for beginners.


Question 19 — Sample Answer:

.copy() creates a new list, but the objects inside that list (such as dictionaries) are still shared references to the same objects in memory. copy.deepcopy() creates new copies of all nested objects recursively, so modifying the copy cannot affect the original.

Example of the bug: if you call .copy() on a list of customer dicts and then modify a customer's tier in the copy, the original database record will also show the new tier — silently corrupting your "backup" copy.


Question 20 — Sample Answer:

new_this_quarter    = this_quarter_customers - last_quarter_customers
churned_customers   = last_quarter_customers - this_quarter_customers
retained_customers  = last_quarter_customers & this_quarter_customers

Results: - new_this_quarter: {"Apex", "Ironwood"} - churned_customers: {"Beacon", "Ridgeline"} - retained_customers: {"Thornfield", "Summit"}

Set operations make what would otherwise require careful loop logic into simple, readable one-liners.


Scoring Guide

Score Range Interpretation
18–20 90–100% Excellent mastery. Ready for Chapter 8.
15–17 75–89% Strong understanding. Review any questions you missed.
12–14 60–74% Solid foundation. Re-read the sections covering your missed questions.
Below 12 Below 60% Review the chapter and work through Tier 1 and Tier 2 exercises before moving on.

For Part C, a full-credit answer demonstrates the correct approach and would run without error. Partial credit for answers that show the right idea but have syntax issues.