Dictionary
In a dictionary we map keys to values. Python dictionaries are maps. With square brackets, we assign and access a value at a key.
Dictionary
methodsWith get()
we can specify a default result. Python is "batteries included," so it provides many helpful methods upon dictionaries.
Here we use the get method and the direct access approach to accessing keys in a dictionary. Using get()
in Python is important—it is one of the first things to learn.
Get()
receives the key you are testing, and (optionally) a value returned if the key is not found.# Part 1: add 2 key-value tuples to the dictionary. plants = {} plants["radish"] = 2 plants["squash"] = 4 # Part 2: direct access. print("radish:", plants["radish"]) # Part 3: get method calls. if (result := plants.get("squash")) != None: print("squash:", result) print("tuna:", plants.get("tuna")) print("tuna:", plants.get("tuna", "no tuna found"))radish: 2 squash: 4 tuna: None tuna: no tuna found
Keys
A non-empty dictionary has keys. Here we create a dictionary of 3 key-value pairs. This dictionary could be used to store hit counts on a website's pages.
hits = {"home": 125, "sitemap": 27, "about": 43} keys = hits.keys() values = hits.values() print("Keys:") print(keys) print(len(keys)) print("Values:") print(values) print(len(values))Keys: dict_keys(['home', 'about', 'sitemap']) 3 Values: dict_values([125, 43, 27]) 3
To begin we show 3 different ways of creating a dictionary with 3 keys and 3 values (3 items). The keys are color names, and values are integers.
iterable
that contains 2-element tuples. We can pass this to the dict
built-in to get a dictionary.# Part 1: use literal expression. colors1 = {"cyan": 5, "peach": 7, "pink": 9}; print(colors1) # Part 2: create empty dictionary, then assign. colors2 = {} colors2["cyan"] = 5 colors2["peach"] = 7 colors2["pink"] = 9 print(colors2) # Part 3: create list of 2-element tuples. # ... Then use dict. items = [("cyan", 5), ("peach", 7), ("pink", 9)] colors3 = dict(items) print(colors3){'cyan': 5, 'peach': 7, 'pink': 9} {'cyan': 5, 'peach': 7, 'pink': 9} {'cyan': 5, 'peach': 7, 'pink': 9}
In Python "None
" is a special value like null
or nil
. We often use None
in programs. It means no value. Get()
returns None
if no value is found in a dictionary.
None
value for "carrot." So get()
can return None
, but there is actually a None
value in the dictionary.lookup = {"bird": 10, "carrot": None} # A value can be none. print("GET:", lookup.get("carrot")) print("GET:", lookup.get("xyz"))GET: None GET: None
A dictionary (and its get method) can be used to count letter frequencies. We use get()
on a dictionary to start at 0 for nonexistent values.
Get()
has a default return.# The first three letters are repeated. letters = "abcabcdefghi" frequencies = {} for c in letters: # If no key exists, get returns the value 0. # ... We then add one to increase the frequency. # ... So we start at 1 and progress to 2 and then 3. frequencies[c] = frequencies.get(c, 0) + 1 for f in frequencies.items(): # Print the tuple pair. print(f)('a', 2) ('c', 2) ('b', 2) ('e', 1) ('d', 1) ('g', 1) ('f', 1) ('i', 1) ('h', 1)
Dict.fromkeys
Suppose we have a list (or other iterable
) of keys, but no values in particular. We can use dict.fromkeys
, and specify a default value.
dict.fromkeys
and then call get()
to search for a couple keys inside the dictionary.animals = ["bird", "frog"] # Initialize a dictionary from a list of keys. # ... Set all values to 100. result = dict.fromkeys(animals, 100) print("RESULT:", result) print("FROG: ", result.get("frog")) print("?: ", result.get("?"))RESULT: {'bird': 100, 'frog': 100} FROG: 100 ?: None
We can use update to initialize a dictionary. We start with an empty dictionary, and then pass other dictionaries to the update()
method.
# Start with an empty dictionary. # ... Initialize it with update() calls. ids = {} print("START: ", ids) # Call update twice. ids.update({"abc123": 30, "def123": 20}); print("UPDATE 1:", ids) ids.update({"xyz987": 40}) print("UPDATE 2:", ids)START: {} UPDATE 1: {'abc123': 30, 'def123': 20} UPDATE 2: {'abc123': 30, 'def123': 20, 'xyz987': 40}
For further illustration, let us consider what happens when we update an empty dictionary. The dictionary gets all the keys and values from the update.
test = {} # Update an empty dictionary. test.update({"bird": 10, "frog": 20}) print(test){'bird': 10, 'frog': 20}
In a dictionary, keys are not sorted in any way—they are unordered. Their order reflects the internals of the hashing algorithm's buckets.
sorted()
, on the keys. This creates a sorted view.# Same as previous program. hits = {"home": 124, "sitemap": 26, "about": 32} # Sort the keys from the dictionary. keys = sorted(hits.keys()) print(keys)['about', 'home', 'sitemap']
Here we call items()
. With tuples, we can address the first element with an index of 0, and the second element with 1. The code uses a for
-loop on the items iterable
.
rents = {"apartment": 1000, "house": 1300} # Convert to iterable of tuples. rentItems = rents.items() # Loop and display tuple items. for rentItem in rentItems: print("Place:", rentItem[0]) print("Cost:", rentItem[1]) print("")Place: house Cost: 1300 Place: apartment Cost: 1000
Items()
can be used in another for
-loop syntax. We can unpack the 2 parts of each tuple in items()
directly in the for
-loop. We use "k" for the key, and "v" for the value.
# Create a dictionary. data = {"a": 1, "b": 2, "c": 3} # Loop over items and unpack each item. for k, v in data.items(): # Display key and value. print(k, v)a 1 c 3 b 2
Sometimes we want to invert a dictionary—change the values to keys, and the keys to values. Complex solutions are possible. But we can do this with items()
and a loop.
reptiles = {"frog": 20, "snake": 8} inverted = {} # Use items loop. # ... Turn each value into a key. for key, value in reptiles.items(): inverted[value] = key print(":::ORIGINAL:::") print(reptiles) print(":::KEYS, VALUES SWAPPED:::") print(inverted):::ORIGINAL::: {'frog': 20, 'snake': 8} :::KEYS, VALUES SWAPPED::: {8: 'snake', 20: 'frog'}
For
-loop exampleHere we see the simplest for
-loop over a dictionary. But by directly looping over the dictionary, we may need to access the values for each later.
get()
in some programs. Using items()
in the for
-loop would avoid this.for
-loop, is the key. The value is not available—we would need get()
to access it.plants = {"radish": 2, "squash": 4, "carrot": 7} # Loop over dictionary directly. # ... This only accesses keys. for plant in plants: print(plant)radish carrot squash
We can call the items()
method to get a list of tuples. No extra hash lookups will be needed to access values. We can unpack each tuple directly in the loop statement.
color_codes = {"blue": 10, "red": 20} # Loop over items(). # ... Unpack into a 2-item tuple. for (color_name, color_code) in color_codes.items(): print("NAME:", color_name) print("CODE:", color_code)NAME: blue CODE: 10 NAME: red CODE: 20
We first create a dictionary, and then create a copy of it. We then modify values within the copy. After copying the dictionary, the original is not affected when we modify the copy.
original = {"box": 1, "cat": 2, "apple": 5} # Create copy of dictionary. modified = original.copy() # Change copy only. modified["cat"] = 200 modified["apple"] = 9 # Original is still the same. print(original) print(modified){'box': 1, 'apple': 5, 'cat': 2} {'box': 1, 'apple': 9, 'cat': 200}
dict
There are other ways to copy a dictionary in Python. The dict
built-in method can be used in the same way as copy()
. This program shows the syntax.
copy()
method on a dictionary to copy the entire contents of the original dictionary.dict
built-in method to copy the dictionary. This has the same effect as the copy()
method.original = {"red": 0, "blue": 10} # Part 1: use copy() to duplicate a dictionary. copy1 = original.copy() copy1["red"] = 1000 print(copy1) # Part 2: use dict keyword to copy. copy2 = dict(original) copy2["red"] = 2000 print(copy2) # Part 3: original is unchanged. print(original){'red': 1000, 'blue': 10} {'red': 2000, 'blue': 10} {'red': 0, 'blue': 10}
Here we invoke dict.fromkeys
on a string
list. If you specify the second argument to fromkeys, each key has that value in the newly-created dictionary.
# A list of keys. keys = ["bird", "plant", "fish"] # Create dictionary from keys. d = dict.fromkeys(keys, 5) # Display. print(d){'plant': 5, 'bird': 5, 'fish': 5}
We can invoke dict.fromkeys
with no second argument. This means that each key has the special value of None
—the keys are still in the dictionary, but have values of None
.
in
-keyword still finds the keys in the dictionary with values of None
.values = [10, 20, 30] # Use fromkeys to create keys with values of None. lookup = dict.fromkeys(values) if 10 in values: print("HAS 10") if 300 in values: # Not reached. print("HAS 300")HAS 10
I compared a loop that uses get()
with one that uses both the in
-keyword and a second look up. Version 2, with the "in" operator, was faster.
get()
. It tests that against the result and then proceeds if the value was found.in
-operator to test the contents of a dictionary. This approach should be preferred when possible.import time # Input dictionary. systems = {"mac": 1, "windows": 5, "linux": 1} print(time.time()) # Version 1: use get. v = 0 x = 0 for i in range(10000000): x = systems.get("windows", -1) if x != -1: v = x print(time.time()) # Version 2: use in. v = 0 for i in range(10000000): if "windows" in systems: v = systems["windows"] print(time.time())1478552825.0586164 1478552827.0295532 (get = 1.97 s) 1478552828.1397061 (in = 1.11 s)
A dictionary can be looped over in different ways. In this benchmark we test 2 approaches. We access the key and value in each iteration.
while
-loop. It then does an extra lookup to get the value.import time data = {"parrot": 1, "frog": 1, "elephant": 2, "snake": 5} items = data.items() print(time.time()) # Version 1: get. for i in range(10000000): v = 0 for key in data: v = data[key] print(time.time()) # Version 2: items. for i in range(10000000): v = 0 for tuple in items: v = tuple[1] print(time.time())1478467043.8872652 1478467048.6821966 (version 1 = 4.79 s) 1478467053.2630682 (version 2 = 4.58 s)
With dictionaries, a special hashing algorithm translates a key (often a string
) into an integer. For a speedup, this integer is used to locate the data.