课程: Python Essential Training

Handling exceptions - Python教程

课程: Python Essential Training

Handling exceptions

- [Instructor] Exceptions are not to be feared but they do need to be reigned in. We saw a little bit about how to do this previously with the Try-Except statement. So here, we are catching this exception and then just returning it. So we don't get a stack trace or anything, but we do see that this zero division error instance is returned. There are a few interesting things you can do with this Try-Except pattern. For instance, if we don't care about getting the specific instance of the exception, we just maybe want to print something. We don't have to have the as e in order to catch an exception. There was some sort of error. And then that just prints out. Another useful thing you can do with this is the finally statement. So if I take my Try-Except block and add a finally to it, this will always execute. And you see that gets printed out. Finally statements can be useful because they will always execute no matter what happens inside this try block. You don't even need any except statements, right? So this error is thrown, but this still printed out. Even if no exception is raised at all, that still executes. I often use them when I'm timing how long a function takes to execute. So if we import the time class up here, import time, we can use this to actually time our function. So I'm going to make a timer. We need the start time, time.time will give you the current time and seconds. And in our finally statement, we're going to make this an f-string and say, Function took, yeah, time.time minus start seconds to execute. And another fun thing we can do with this is time.sleep. And time.sleep just pauses execution for some number of seconds. In this case, we're going to pause it for half a second. Let's see how long that takes. So you can see that half second and then that very fast execution there. If I change the statement to something that causes a zero division error, you see that timer still happens. This try-finally pattern keeps your code clean and compact and lets you do any needed cleanup or logging after a statement completes no matter what happens inside the try block. But back to exception catching up here. So I'm going to just grab this. Notice that I'm always catching this exception class. I could add another except statement above this. You can chain these together just fine. Zero division error. So we're going to catch that one specifically, print There was a zero division error, and now that prints out. I could also add say a type error statement, except type error. All right, let's print There was a type error. But of course there was a zero division error, not a type error. Let's maybe cause a type error by trying to add an int to string. Now there was a type error. The order of these except statements does matter here. So Python will try the first one. Say, does this match type error? Yes, and it doesn't bother with the other ones. So if I move this general exception up, so this is the class from which all of these extend, there was some sort of error. So you always want your most general exceptions down here. And then the more specific ones up top. Sometimes you're doing really involved exception handling and catching. For instance, I see this a lot with HTTP request response handling where there are a lot of different types of HTTP errors and you'll have a lot of except statements all in a row. And then you'll just copy and paste all of these different blocks into many different functions. And in this situation, it can be really handy to move this trying and catching into a single function. And you can also use a custom decorator to do this. We saw decorators previously with the static method decorator but now we're going to write our own. So let me grab these all these exception handlings that we've done and I'm going to make a new function called handleException. And we're going to pass as an argument, a function. And then we're going to define an inner function in here called wrapper. We're going to try to execute this function that was passed in and then we're going to paste our exceptions here. Whoops, there we go. Okay, now we return this wrapper function. Now we can use this. So let's make a decorator handleException and put it on a causeError function. And this is just going to return one over zero. Now if we call causeError, you see that this handle exception was used to except those various exceptions that this could throw. And we can of course reuse this decorator for another function. Let's talk about raising exceptions. Let's use our handle exception decorator. Make a a function called raiseError raise Exception. This raise statement that I've used raises or throws this new exception that I'm creating when it's reached. For instance, we can turn this into function that excepts any input except the number zero. So let's add an argument here and say if n is equal to zero, we raise an exception. Otherwise, we print n. And notice that I'm not using an else statement here, right? I don't need it because once the exception is raised here, this execution will just halt, it'll throw this exception, and then the print n will never be reached. So we don't need to bother with an else statement there. Now there is one problem with this. Notice up in our handleException function, this is the function that gets passed in, this raiseErrors being passed in. But when we call it, there aren't any arguments even though our function has an argument. So now we're trying to use this handler on a function that takes arguments. So we need to modify it a bit using the variable args. Remember that? And we can pass our args to this function there. So let's rerun that. And now if we do that, there is some sort of error. Now we get an error printed out if the argument we pass in is zero, otherwise it just prints out the value. Writing your functions to be able to raise exceptions is especially powerful when you combine it with custom exceptions. So let's go take a look at those next.

内容