main
No variables
One dictionary can describe one session. A list of dictionaries can describe several sessions:
sessions = [ {"subject": "Programming", "minutes": 25}, {"subject": "Math", "minutes": 40}, {"subject": "Programming", "minutes": 15}]The outer list preserves the session order. Each dictionary keeps a subject and a duration together. The two levels have different jobs.
Consider:
first_session = sessions[0]print(first_session["subject"])sessions[0] retrieves a dictionary. Looking up "subject" in that dictionary retrieves a string. The shorter expression does the same two steps:
print(sessions[0]["subject"])Read nested access from left to right. Do not treat two sets of brackets as an entirely new operation.
Direct iteration now produces records:
def total_minutes(sessions): total = 0
for session in sessions: total = total + session["minutes"]
return totalThe total pattern has not changed. The loop obtains the relevant number from a field rather than receiving that number directly.
Assume each record has a "subject" string and a positive integer "minutes". A function can state that input contract without checking every type on every call. Part 5 will examine validation when records come from files rather than controlled examples.
Now write a function that selects session subjects using their durations. Follow the same two steps: read a record from the list, then look up the fields you need.
0 / 15 points
Write subjects_for_long_sessions(sessions, minimum). Each record has a subject string and positive integer minutes. The minimum is a nonnegative integer.
Return a new list of subjects for sessions lasting at least the minimum, preserving order and repeated subjects. Return [] for no matches. Do not print or change the records.
For [{"subject":"A","minutes":30},{"subject":"B","minutes":10},{"subject":"A","minutes":40}] and 30, return ["A", "A"].
Position can change when a list is reordered. An identifier gives a record a name that need not change with its position:
items = [ {"id": "E10", "name": "Camera", "available": True}, {"id": "E20", "name": "Tripod", "available": False}]
def find_item(items, item_id): for item in items: if item["id"] == item_id: return item
return NoneThe contract assumes unique identifiers. It returns the matching dictionary itself, or None when no item matches. It does not create a copy.
This matters:
found = find_item(items, "E10")
if found != None: found["available"] = False
assert items[0]["available"] == FalseThe local name and the list item refer to the same dictionary. This is the aliasing from Part 3, now inside a collection.
0 / 15 points
Write mark_available(items, item_id). Items are records with unique id strings, name strings, and available booleans. Identifiers are case-sensitive.
If an item with the exact identifier exists, set its available field to True and return True, including when it was already available. If no item matches, return False and change nothing. Update the existing dictionary so that other names for it observe the change. Preserve every other field and record, including any extra fields. Do not print.
This function creates a new list, but not new records:
def select_long_sessions(sessions, minimum): result = []
for session in sessions: if session["minutes"] >= minimum: result.append(session)
return resultAppending or removing an item in result does not add or remove an item in sessions. Updating a dictionary inside result, however, changes a dictionary that both lists can reach.
A new outer list does not imply independent contents.
That is not automatically a bug. It depends on the interface promised by the function.
Here is the filtering loop with two input records and a minimum of 30 minutes. Follow the reference appended to result: updating that record is visible through sessions, but appending another record changes only the result list.
Code
Step 1 of 13
State
No variables
Objects
No objects yet
Output
No output yet
In the following questions, distinguish changes to a list from changes to a record inside it. Follow which access paths reach the same dictionary before deciding what a caller can observe.
The dictionary method .copy() creates a new dictionary with the same entries:
Here all values are strings or integers. Replacing a value in copy does not replace the corresponding value in original.
A filtering function that promises independent flat records can append a copy:
result.append(session.copy())This is a shallow copy: the outer dictionary is new, but its values are the same objects. That distinction will become important when a field contains a list in the next chapter.
0 / 15 points
Write copy_long_sessions(sessions, minimum). Records contain exactly a subject string and positive integer minutes; minimum is a nonnegative integer.
Select sessions lasting at least minimum, preserving order and repeated records. Return a new list containing a new dictionary for every selected session. Changing the result or any returned record must not change the input. Return [] for no matches. Do not print.
For one 25-minute session and minimum 20, the result initially has equal contents to the input, but changing the returned minutes must leave the input at 25.
Explain which objects each result shares with its input, and show how changing a returned record reveals the difference.
0 / 10 points
The list records contains at least two dictionaries. Each dictionary contains only a "name" string and integer "points". One function returns [records[1]]; another returns [records[1].copy()].
In a short explanation, compare appending to each returned list and changing the returned record’s points. Give an example with expected original and returned values, and explain which version fits a caller that needs independently editable records.
find_item return when it finds a match?