Organizing Data

Dictionaries and Named Information


Learning Objectives

  • You can create a dictionary and access, add, and update named values.
  • You can distinguish dictionary keys from list positions.
  • You can handle an optional field without hiding a missing required field.
  • You can apply the names-and-objects model to a mutable dictionary.

A list connects positions with values. A dictionary connects keys with values. Keys let a program refer to information by a meaningful name rather than by its position.

Give Each Field a Name

Run this example:

No files opened Select a file to edit

The braces enclose the dictionary. Each entry has a key, a colon, and a value. Commas separate entries.

Here, "title", "pages", and "finished" are keys. Their associated values have different types: a string, an integer, and a boolean. Together they describe one book. A collection of named fields describing one thing is often called a record.

Use square brackets to look up a key:

book["pages"]

The brackets are familiar, but the meaning is different from a list index. Python looks for the key "pages"; it does not look for a position.

The records in this part use strings as keys. Other key types, such as integers, are also possible. The examples here concentrate on descriptive strings and stable string identifiers.

Change or Add Information

Assignment can replace the value associated with a key:

book["pages"] = 192
book["finished"] = True

If the key does not exist, assignment adds it:

book["author"] = "Mira North"

There is one value for each key. Assigning book["pages"] again replaces that key’s previous value; it does not add a second "pages" field.

You can also begin with an empty dictionary:

book = {}
book["title"] = "The Quiet Forest"
book["pages"] = 180

Use {} for an empty dictionary and [] for an empty list.

A function can receive a dictionary as an argument, just as it can receive a list. Updating an entry changes the dictionary supplied by the caller.

Finish a session

0 / 10 points

Write finish_session(session, extra_minutes). The dictionary has a subject string, nonnegative integer minutes, and a completed boolean. extra_minutes is a nonnegative integer. Increase the existing minutes by that amount and set completed to True. Change the supplied dictionary, keep all other fields, and return None. Do not print.

For {"subject": "Math", "minutes": 20, "completed": False} and 10, the same dictionary must afterwards contain 30 minutes and completed True. Define the function in main.py. Use temporary calls while testing, then remove them before submitting.

Membership Checks Keys

A dictionary may not contain every field a program could ask about:

book = {"title": "The Quiet Forest", "pages": 180}
print("title" in book)
print("The Quiet Forest" in book)

The output is:

True
False

in checks the keys, not the values. To compare the title’s value, use book["title"] == "The Quiet Forest".

len(book) counts its entries.

Missing Information Is a Design Decision

Looking up a missing key with brackets raises KeyError:

book = {"title": "The Quiet Forest"}
print(book["author"])

This example deliberately fails. The record has no "author" key.

Sometimes a field is required. A missing required field then indicates a problem with the data or the code that created it. Sometimes a field is optional. The program can handle that case explicitly:

if "author" in book:
print(book["author"])
else:
print("Author not recorded")

For an optional lookup, .get(...) can supply a fallback:

print(book.get("author", "Author not recorded"))

This returns the value for "author" when the key exists, and the second argument otherwise. It does not add the missing key. Without a second argument, .get(...) returns None for a missing key. You met None in Part 3 as a value that can represent the absence of a result.

A fallback is not validation. Using book.get("pages", 0) could hide a missing required page count by turning it into a plausible number.

Describe a location

0 / 10 points

Write location_text(session). The dictionary has a required subject string and may have a location string. Return the location when the key exists, or "Not recorded" when it does not. An existing empty string must be returned unchanged. Do not print or change the dictionary.

For {"subject": "Math", "location": "Home"}, return "Home". For {"subject": "Math"}, return "Not recorded".

Dictionaries Are Mutable Objects

The names-and-objects model from Part 3 still applies:

first = {"title": "The Quiet Forest", "pages": 180}
second = first
second["pages"] = 200
assert first["pages"] == 200

Assignment did not make a copy. Both names refer to the same mutable dictionary.

A function can deliberately update a record:

def add_minutes(session, extra_minutes):
session["minutes"] = session["minutes"] + extra_minutes
session = {"subject": "Programming", "minutes": 20}
add_minutes(session, 15)
assert session["minutes"] == 35

This function’s job is mutation. It has no explicit return, so its returned value is None. Rebinding the local parameter to another dictionary would not replace the caller’s dictionary, just as rebinding a list parameter did not replace the caller’s list.

Step through the call. The two names session belong to different frames, but both refer to the same dictionary. Follow the "minutes" entry when the function changes it and when the local frame closes.

Code

Step 1 of 6

1 / 6
Formatting...

State

main

No variables

Objects

No objects yet

Output

No output yet

Use the same distinction between names and objects in the following questions. Check which operations only retrieve information and which change the record, and consider whether a missing field should produce a fallback or an error.

Check Your Understanding

  1. How does book["pages"] differ from values[0]?
  2. What happens when assignment uses a key that already exists?
  3. What does "title" in book check?
  4. When might a default value conceal a problem rather than solve one?
  5. Why can a change through second also be visible through first?