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
.popitemmethod removes the most recently inserted item, returning it as a(key, value)tuple. As with the list.popmethod, 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:
4
In the next example, remember that
.popitemreturns a(key, value)tuple.- Note: this code example reuses elements (variables, etc.) defined in earlier examples.
>
food_order_totals.popitem()Result:
('Dalili', 1000) - Note: this code example reuses elements (variables, etc.) defined in earlier examples.
>
len(food_order_totals)Result:
3
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 = 0while len(food_order_totals) > 0:name, individual_total = food_order_totals.popitem()# Don't include Amir's spending.if name == "Amir":continuetotal += individual_total- Note: this code example reuses elements (variables, etc.) defined in earlier examples.
>
totalResult:
1000
- Note: this code example reuses elements (variables, etc.) defined in earlier examples.
>
food_order_totalsResult:
{} When we only need a stack, using a list with
.popand.appendis 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
.fromkeysmethod 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_scoresResult:
{'red': 0, 'blue': 0}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']
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:
[]
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
.fromkeysbehavior 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_membersResult:
['Amir']