Write Pythonic Code Like a Seasoned Developer Transcripts
Lecture: Safer dictionary item access
0:01 One of the most common actions that we perform at dictionaries is
0:03 to read values from them and see what they are storing.
0:06 So let's look at the variety of ways we can do this in Python.
0:09 Here we have a dictionary about a movie,
0:11 information about how long it was, what its title was,
0:14 when it was made, things like that.
0:15 We are going to go and access things like what was the title,
0:18 or what was the year and see how we do that.
0:21 So let's go over here and try what I am going to call the optimistic style,
0:25 so we might want to print out the year,
0:27 so we can come here and I can just say (data['year'])
0:31 and we should get back this,
0:34 and let's go ahead and make that a number while we are at it.
0:37 So, if I run this you should see at the top "2001",
0:41 that works great if the value is in there,
0:43 but if I ask for something like the rating,
0:45 and there is no rating for this movie, not so good.
0:48 so we get KeyError "rating".
0:51 So let's comment that out, so that we can get to the rest of our app.
0:54 So you might say well, that didn't work out so well for me,
0:56 let's be a little more careful, pessimistic,
1:00 let's assume it's going to not work and put a lot of error handling around it,
1:03 so we could print, we could do basically the same thing here,
1:07 we could print out the year, we could print out the rating,
1:09 and we could catch the error and just print it out like this.
1:17 Notice the PyCharm thinks "Ooops", it's misspelled,
1:19 it should be "Oops" apparently.
1:22 All right, so let's try this, awesome, so we see "optimistic style", "2001",
1:27 and if we try to do this, remember,
1:28 it crashes because our optimism was misplaced,
1:31 so down here we again,
1:33 we get the year and instead of crashing, we actually catch the exception,
1:36 we say oh, there is a KeyError "rating".
1:39 All right, so that's one way we can do it,
1:41 this makes it pretty hard though, there are a lot of times
1:44 when you want to create concise little expressions,
1:47 and you want to use the values that come out of these dictionaries,
1:50 think of a list comprehension or something like that,
1:53 especially on data science, these are very common
1:56 and this format just entirely breaks this fluent expression functional flow,
2:01 so we can do other things instead,
2:04 now if we want to know for sure whether an item is in the dictionary
2:06 we can just ask first so we could say "if year is in data", then we'll print data of year.
2:15 Similarly, same thing for rating, and it's not just so we can
2:21 detect it in this case let's print out, "Oh, we didn't find the rating...",
2:25 all right, let's run and see how that works.
2:28 OK, safety first, we checked yes,
2:32 it was true that year was in there so we want its value printed out,
2:35 we asked if rating was in there, no it wasn't
2:37 and so oh we didn't find the rating, is what we found out here.
2:41 Now this "safety first style" here lets us check first
2:45 and then use the data if we are willing to just say "look,
2:49 I am willing to accept None or sort of a missing value"
2:52 we can use a different style and I actually like this style,
2:55 I have started to use it more and more
2:56 because it lets me test a variety of things all at once.
3:00 So down here I can say instead of data bracket where I index in
3:03 and of course if the value's missing I get a key error, I can say "get",
3:06 and notice it has a key and a default.
3:09 So if we come in here, we can say the key is going to be
3:11 again year and the default is None, so if there is no year, fine just give us None.
3:17 Same thing for rating, so let's run those again.
3:20 Like before, we got the year but now instead of a crash,
3:23 we have some kind of a exception handling or "if...else" block,
3:27 we just get None and that's actually often quite useful.
3:30 Though, sometimes None is not what we want.
3:34 So this style up here works well if we are willing to accept None
3:37 but here we have a year and that's supposed to be a number,
3:40 what if we would like to have say zero or negative one or something like that
3:43 for the year if it doesn't exist, we could use our "if...else" block
3:47 or something like that but actually we can just come down here
3:50 and say let's get zero if the year isn't there and for the rating,
3:54 let's suppose we are just going to get a one star,
3:57 or maybe an empty string or something like that, we'll go with one star for now.
4:00 Or maybe so it doesn't look so bad we'll say "3 out of 5 stars", something like that,
4:05 so if we don't specify rating, we'll get this.
4:08 this is a little bit contrived, but at least you'll see how it works.
4:12 And we explicitly ask for it but supply an alternative,
4:14 we get 2001 because of course the year is there,
4:17 but the rating which is missing just gives us
4:19 what we would assume to be the default unrated value we'll say that's 3 stars,
4:23 we could maybe make that empty, or who knows.
4:26 This style is really nice, however, it requires that we specify
4:29 the alternative every single time we call "get"
4:31 what if we would like whenever somebody uses our dictionary a different default,
4:36 so there is a thing in collections called "defaultdict",
4:41 we can import that up at the top and down here we'll have data,
4:45 we'll just sort of replace data with this defaultdict,
4:49 so we need to give it a couple of things,
4:52 one thing we have to give it a callable that will return the default value,
4:56 not the default value itself but a thing that will create the default value,
4:59 so we can use a lambda, and we can say
5:02 when somebody needs to create a default value,
5:04 let's say "MISSING", so here it looks little funky maybe to you,
5:07 but this is the function and when you call it, it returns the value missing.
5:12 Next, let's make sure that we copy the existing data into this dictionary
5:16 and then we'll replace it with this default version of itself,
5:19 let's just print out data to make sure that that's sort of transformation worked.
5:23 All right, here is the defaultdict with the lambda function,
5:26 and the data we expect, beautiful.
5:28 So let's try this exact same thing again but avoid supplying any of the values there,
5:34 if I would run it, we'd expect the year to come out as 2001 as it has every time,
5:38 and we would expect rating to require whatever the missing value is
5:43 so it should say "MISSING".
5:46 All right, I always make this mistake, even on defaultdicts,
5:49 when you call "get", you are still going to get the default value,
5:52 which is the default parameter set to None,
5:55 the defaultdict behavior comes in, when you access it like this,
5:59 I almost edited that out but it seems like I make that mistake often
6:01 and you may make it as well so hopefully it's helpful to see it.
6:05 There we go, year is 2001, and instead of crashing, we get "MISSING" for rating.
6:12 All right, so that gives you the spectrum of ways to access data from dictionaries,
6:16 Pythonically in a safe way and you saw that Python gives you many options
6:20 to control the spectrum of the behavior you want,
6:23 do you want to make sure it crashes right away if you try to get a value it's missing,
6:26 fine you can do that, do you want to always supply default value,
6:29 you can do that, and so on.
6:31 So, we started out with this dictionary,
6:33 and we said let's just try to get the year and rating from it like so,
6:36 year was totally fine, but rating, not so fine, remember we got the crash,
6:41 KeyError 'rating', we said OK, well let's try it this way,
6:43 let's use ".get" and we'd like to specify an alternative value
6:47 so for year we said the alternative value is going to be zero, and then in this case,
6:51 we are going to say if you have no rating, please provide -1.
6:56 And you can see, because there was a year we got the value,
6:58 there is no rating, we got -1.
7:00 This kind of default is really helpful because then
7:02 if you know you are going to parse that to an integer, or number,
7:05 something like that, you can provide a default
7:07 that's always going to parse, rather than None.
7:09 Finally, if we want to set this behavior universally across a given dictionary,
7:13 we can use defaultdict from the collections module,
7:16 we specify a function that will return the value when called
7:20 and here we are passing in the original data to sort of transform
7:23 the basic dictionary into a defaultdict dictionary.
7:27 Now when we access data out of our dictionary,
7:29 we ask for year, we get the year value because it's there.
7:33 If we ask for rating, which is not in there,
7:34 it's going to call our lambda to supply the default value,
7:37 that will return missing.