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