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