Execute Program

Python for Programmers: Even More Dict Methods

Welcome to the Even More Dict Methods lesson!

This lesson is shown as static text below. However, it's designed to be used interactively. Click the button below to start!

  • We've already seen a number of built-in dictionary methods. In this lesson, we'll see a few more!

  • The .popitem method removes the most recently inserted item, returning it as a (key, value) tuple. As with the list .pop method, this mutates (changes) the original dictionary.

  • >
    food_order_totals = {
    "Amir": 500,
    "Betty": 400,
    "Cindy": 600
    }
    food_order_totals["Dalili"] = 1000
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    len(food_order_totals)
    Result:
    4Pass Icon
  • In the next example, remember that .popitem returns a (key, value) tuple.

  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    food_order_totals.popitem()
    Result:
    ('Dalili', 1000)Pass Icon
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    len(food_order_totals)
    Result:
    3Pass Icon
  • We often say that .popitem() returns the value in LIFO (Last In, First Out) order. This means that our dictionary can be used like a stack. We continually pop the most recent item from the dictionary, process it, and then continue on to the next item.

  • The next example iterates through dictionary items, removing one item at a time while adding each dictionary value to total. Note that we skip Amir's food order (he had to go home last minute).

  • >
    food_order_totals = {
    "Amir": 500,
    "Betty": 400,
    "Cindy": 600
    }

    total = 0
    while len(food_order_totals) > 0:
    name, individual_total = food_order_totals.popitem()
    # Don't include Amir's spending.
    if name == "Amir":
    continue
    total += individual_total
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    total
    Result:
    1000Pass Icon
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    food_order_totals
    Result:
    {}Pass Icon
  • When we only need a stack, using a list with .pop and .append is more clear and more idiomatic. But when we need to access the data in both the dictionary's keys and values, treating it as a stack can be useful.

  • The .fromkeys method creates a dictionary where many keys map to the same value. It takes a list of keys as the first argument and a value as the second argument.

  • >
    team_names = ["red", "blue"]
    team_scores = dict.fromkeys(team_names, 0)
    team_scores
    Result:
    {'red': 0, 'blue': 0}Pass Icon
  • The dictionary value in the example above is 0, which is immutable. It's important to only use this method with immutable values like strings, ints, floats, and booleans. Using a mutable value can cause subtle bugs because every key will correspond to exactly the same value, at the same place in memory.

  • Here's an example showing the problem. This time, our value is a list. When we change that list via one dictionary key, the list in the other keys also changes. That's because it's the same list!

  • >
    team_names = ["red", "blue"]
    # Each key here references the same list as its value!
    team_members = dict.fromkeys(team_names, [])

    # The dictionary has two keys, but both point to the same list. If we modify
    # the list via one key, our changes will also show up via the other key.
    team_members["red"].append("Amir")
    team_members["blue"]
    Result:
    ['Amir']Pass Icon
  • That's a bug: adding Amir to the red team shouldn't also add him to the blue team. This is similar to the problem that we saw in an earlier lesson on mutable default argument values. When we mutate a list's contents, that mutation is visible to any code that can see the list. In this case, changing the list via one dictionary key is also visible via the other key, because both keys refer to the same list in memory.

  • There are many ways to work around this. One way is to build our dict with a dictionary comprehension.

  • The next example adds a dictionary item for every team name in team_names. The key is set to the team name, and the value is set to a new empty list, [].

  • >
    team_names = ["red", "blue"]
    team_members = {team_name: [] for team_name in team_names}
    team_members["red"].append("Amir")
    team_members["blue"]
    Result:
    []Pass Icon
  • In that example, the comprehension iterates twice, once per team name. During each iteration, Python evaluates the [] expression, giving us a new empty list. Our dict now has two different lists as its values, so updating one doesn't affect the other.

  • When we do dict.fromkeys(team_names, []), why doesn't Python just make a new list for us each time?

  • One good reason is: sometimes we want a dictionary where multiple keys correspond to exactly the same list in memory! Python needs to allow that.

  • Although Python prioritizes ease of use, this doesn't necessarily mean that it guesses what the programmer wants. Sometimes we want to do something that the language designers never thought of, or even something they would find objectionable.

  • This .fromkeys behavior is also consistent with what we see when we assign a list to two variables, then change the list via one of them.

  • >
    red_team_members = blue_team_members = []
    red_team_members.append("Amir")
    blue_team_members
    Result:
    ['Amir']Pass Icon