Write Pythonic Code Like a Seasoned Developer Transcripts
Chapter: Methods and Functions
Lecture: I'm going to ignore your return value
0:01 In programming, there is several philosophies on how you deal with errors.
0:04 One hand, you could look at all the various cases,
0:08 check all the return values and do a lot of tests before you take any action,
0:12 to try your best to make sure that action will be successful.
0:16 This type of error handling is popular in languages
0:18 that are not great at supporting exceptions, such as C.
0:22 Languages that do support exceptions lean more towards
0:25 what you might consider an optimistic style of API,
0:28 where they try things and if something goes wrong
0:30 they will catch the errors and deal with it.
0:32 So let's look at how this shows up in Python.
0:36 All right, so we have this little support file and in here it has a couple of things,
0:40 support module, it has a download file and if you attempt to download a file
0:45 you need a bunch of things to line up in your favor,
0:48 you need the network to be connected,
0:50 you need the download URL to in theory be set,
0:53 you need the DNS to be active, and you need to actually have access
0:55 to the file you are trying to download.
0:58 Over here, we can just say "well let's just try to download this file
1:01 and see what we get", let's run it.
1:03 Oh, cool, we download some data and it was a binary array saying "cool",
1:07 but what if something goes wrong, let's go over here and fiddle with it,
1:10 like let's say, let's turn off the network,
1:13 now how is this going to work if we run.
1:16 Not so amazing, "ConnectionResetError("Cannot connect to the network")",
1:21 oh, OK well, if we were coming from a C language,
1:27 a C-style language or these languages that typically
1:30 don't have great exception support,
1:33 even if they do have exception support, often it's not well used
1:36 as an idiomatic way of programming, think C++ for example,
1:39 you will see lots of people write algorithms that look like this,
1:43 we'll say oh we've got to make sure the network is working
1:46 so we'll say "if s.check_network()",
1:50 maybe we'll at least put some guarding clause
1:52 so we don't have that nested thing we spoke about earlier,
1:55 so we'll print out something like "cannot download, no network".
2:01 And we'll just bail, so let's run this again, excellent, OK.
2:05 So, "cannot download, no network",
2:07 we didn't crash we just caught this error,
2:09 now there is some other cases we should check,
2:13 we can check the DNS, we can also check the URL.
2:19 OK, still network is turned off, let's go turn that back on,
2:25 we will turn off, the let's see, the DNS now, oh "can't download, no DNS",
2:31 so here is the question, if I look at this code,
2:33 is this code going to deal with all the possible errors?
2:37 Well, it turns out in several ways no, no it's not,
2:41 so first of all, we forgot to check this one, say that's true,
2:46 so we thought we did all the tests it looks like we did a lot of tests at least,
2:50 we are running and crash, still no go,
2:53 because we don't have permission to that file and it turns out
2:55 if we are doing real network I/O deep down within Python
2:58 there is all sorts of exceptions that can be raised,
3:01 so the message, the takeaways basically even if you try to write this code
3:05 they still deal with all the cases you need to put this into a "try...except" block.
3:09 OK, well if you are going to go and put into a "try...except" block,
3:12 why don't you just skip all of this stuff,
3:15 use the programming model that's called
3:17 "it's easier to ask for forgiveness than permission",
3:20 try it, if it fails we'll say "well, sorry, catch, handle the error",
3:23 because it's kind of the code we are going to have to write anyway,
3:26 so let's write a better version, a more Pythonic version here,
3:29 so we are going to say "run_with_checks()" and let's first turn everything back on,
3:33 so this download works, so down here we have run with error handling,
3:37 it's going to crash, well, if I had an error with crash,
3:41 because we have no error handling,
3:43 so let's go over here and we'll put a "try...except" block,
3:46 so we'll say we want to catch exception and maybe even get the details like so,
3:50 and we'll print "Cannot download...", something like this.
3:58 So, let's go ahead and run it, remember the download is working right now,
4:02 so this will just do the same thing, perfect, it does, let's go mess with this,
4:06 let's go say turn off the network, and run it.
4:10 "Cannot download, ConnectionResetError", "Cannot connect to network",
4:15 OK, that's not great, let's put this back on but it didn't crash, we did catch it,
4:20 that's cool, let's say go over here to the permission one
4:23 "Cannot download: Permission error", "Cannot access resource (permission denied)".
4:27 This is cool, this is much more Pythonic than what we have above here, right,
4:32 one thing we are not doing is we are not doing anything different based on errors,
4:35 now technically we are displaying a message that varies by error to the user
4:39 but often you can do different things based on the type of whatever you've got.
4:43 We might want to deal with DNS and network errors differently
4:47 than we would deal with say a permission issue,
4:50 which also might be dealt with differently
4:52 than if we had like the download URL not set.
4:55 So let's go down here and write this last version,
5:01 so let's just see the types of errors we get,
5:03 so I can get a permission error, maybe I want to run some particular code
5:08 if there is a permission error so we could, say, add another except block here
5:11 "PermissionError as pe" and then I could we don't even necessarily need the details,
5:16 we just need the time, so we could print something like
5:18 "Cannot download, you don't have permission...".
5:23 Theoretically, you would do something different here,
5:26 we also saw that there were connection errors,
5:30 here like so, we can print "Cannot download, problem with network";
5:36 and maybe we could put the details in here like this,
5:43 "Cannot download, you don't have permission...",
5:45 so excellent, this code is running, now if we have a different type of error,
5:50 say there is a problem with the network
5:53 "Cannot download, Problem with network: Cannot connect".
5:57 What about DNS - "problem with network, no DNS".
6:02 So there are two Pythonic takeaways here,
6:04 one - this is how you do error handling in Python,
6:08 much less like this, although there are places
6:11 where this makes sense, it's not the primary way of doing it.
6:14 And if you are creating APIs, don't depend on the return value and lots of checks,
6:20 instead just try do evaporation and raise errors properly,
6:24 make sure that you raise different types of errors
6:26 so that we can do different types of responses based on those errors.
6:32 So here is what you call a "look before you leap style of programming",
6:37 it's popular in C and C++ and those types of languages,
6:40 you do a bunch of checks, and you try to test every possible case
6:43 and then you perform the operation and hopefully
6:46 you have tested everything you need to and it doesn't just crash on you right.
6:49 In Python, we typically don't write code like this,
6:52 instead, we write code like this, we just assume that it's going to work
6:56 so we'll just do the most cleanest, simplest, straightforward way of downloading file,
7:01 how do you want to do that? "s.download_file()", boom, done,
7:04 but, something goes wrong, we put it into "try...except" block,
7:07 handle the error and we can even use different error types,
7:10 most specific to most general, to deal with specific errors,
7:13 like for example here we are dealing with the class of network errors
7:17 that have to do with DNS and basic network connectivity.
7:21 This is called
7:22 "it's easier to ask for forgiveness than permission" style of programming.
7:26 And this is more Pythonic than the prior version.