Built-in dict

00:00 In the previous lesson, I gave an overview of the course. In this lesson, I’m going to start the section on dictionaries and focus on the built-in type called dict.

00:11 The purpose of a dictionary is to store data by mapping a key to a value. Some examples of this are mapping a name to a phone number or an IP address to a domain name. Given a key store like this, you could take the name from the data, look it up, and get the phone number as a result.

00:30 Same with the IP address and the domain name. Dictionary data structures are known by a bunch of different names in computer science. Python uses the native term dict for this kind of data structure, but other languages will refer to these as maps, hashmaps, lookup tables, and associative arrays.

00:50 Python builds the dict type straight into the language—and in fact, it does more than that. A lot of class concepts and other ideas inside of Python are built on top of the dict.

01:01 It’s one of the most fundamental aspects of the language at the heart of its implementation. Python uses curly braces to indicate a dictionary. Here’s an example of a dictionary mapping people’s names to their phone extensions.

01:22 This dictionary contains three keys, the strings "bob", "alice", and "janet", and each key maps to a four-digit number, the extension. Inside of the REPL, if I type the name of the phonebook, it gives me what’s called a repr, or the representation, of that.

01:41 It’s an automatic way of translating something to a string that shows you—typically, for debug purposes—the contents of the data structure. I can access any one of the keys by referencing it using square brackets and the name of the key.

02:00 Here, I’m accessing "alice" and getting back the result 3719the extension mapped to "alice" in the dictionary.

02:11 If I try to ask for a key that’s not there, I’ll get an exception. The exception is a KeyError, indicating that that key does not exist inside of the dictionary. In addition to using the square brackets to access a key, I can use the .get() method on the dictionary.

02:29 This ends up with the same result, returning the 3719 we mapped to the key "alice". .get() is subtly different from using the square brackets, in that it doesn’t actually trigger a KeyError. If I call .get() with "fred",

02:46 I get back nothing. The REPL doesn’t show you None when you get None back, so I have to print it in order for you to see that.

02:58 So depending on what trying to do in your code, you can either use the .get() method and check for None when it comes back, or you can use a try/except block to catch any sort of key errors.

03:08 The .get() method has an optional second parameter that allows you to specify the default value to come back instead of None if the key is not there.

03:20 This is a really handy way of getting something out of a dictionary and returning a default value if the key didn’t exist. This is far more succinct than using a try/catch block, catching the KeyError, and then returning a value.

03:35 Running the len() (length) function on phonebook returns the number of keys in the phonebookin this case, 3.

03:41 I can see all of the keys in the phonebook using the .keys() method on the dictionary. This returns a special generator called dict_keys, which contains the values of 'bob', 'alice', and 'janet'.

03:54 If you iterate over the return value of this function, you will get the keys themselves. In addition to having a .keys() method, the dictionary also has a .values() method.

04:06 This is a similar idea, but instead of spitting back the keys, it spits back the values. If you want to iterate over both the keys and the values, the .items() method allows you to do this.

04:20 A common pattern for using this iterator is inside of a for loop getting both the key and the value out of a function and doing something with the contents.

04:35 Here, I’m getting back two values—the name and the extension—from each turn through the iteration.

04:47 I can then print those out to the screen and get back all of the keys and values. In addition to explicitly defining the values like I did in the phonebook, Python supports a mechanism called comprehensions.

05:02 A comprehension is a way of doing a calculation that results in a dictionary. Assume for the moment you had a list of tuples with the names and ranks of your favorite Star Trek characters.

05:20 A dictionary comprehension constructs the dictionary based on some calculation. The comprehension looks like a bit of a combination between the iterator part of a for loop and the brace brackets themselves for a dictionary.

05:42 When trying to understand a comprehension, I like to start from the outsides of the sandwich. On the left side, you see name:rank. That says you’re going to be creating some key-value terms—in this case, based on the ideas of the variables name and rank. On the other end of the sandwich, on the right-hand side of the statement, is the ranks list, which is defined above.

06:05 So you know that you’re going to be creating the dictionary based on the contents of ranks. The middle part of the sandwich is similar to a for loop.

06:14 It creates two variables, name and rank, based on iterating over the ranks list. The resulting dictionary shows a mapping between the names and ranks of Star Trek characters.

06:29 Let’s take a look at another comprehension, this time with a calculation in it.

06:38 Once again, start on the left-hand side. This says it’s going to be creating a dictionary mapping x to the value of x * x, the square of x. On the right-hand side, you can see that you’re going to be mapping values in the return from the function range(6). That’s the values from 0 to 5.

06:59 range() is not inclusive. The middle part of the comprehension says “Loop over those values.” The end result: 0 mapping to 0, 1 to 1, 2 to 4, 3 to 9, 4 to 16, and 5 to 25the value and its corresponding square.

07:19 You can’t just use anything for a key in Python dictionaries. Keys must be hashable. Something is hashable in Python if it’s immutable, which means it can’t change during its lifetime.

07:30 And it implements .__hash__() and can be compared with .__eq__() (equals). The most important part of this is two values that are equal end up having the same hash value.

07:44 That way, when key-values are pushed into the dictionary and accessed using a different variable with the same contents, the result gets returned. Common hashable types built into Python are strings, numbers, and tuples of other hashable types. If you stick to these kinds of keys, you don’t have to worry about .__hash__() or .__eq__(), but if you’re going to build your own objects that you want to be hashable, then you’ll have to go off and implement those dunder methods.

08:14 As I mentioned earlier, the dict type is a foundational part of Python, and it’s key to how Python actually implements a lot of concepts inside of the language. As a result, it’s highly optimized and written to be very performant.

08:28 The attributes of a class and stack frames inside of an execution stack both use the dict type inside of Python. A particular emphasis has been put on performance for the dictionary.

08:41 It is O(1) (order 1) complexity for lookup, insert, update, and delete operations. That’s about as good as it gets. In addition to the built-in type, Python has other kinds of dictionaries as well.

08:55 In the next lesson, I’ll be talking about the OrderedDict and the defaultdict.

Become a Member to join the conversation.