Translated from a * Q&A about exception handling in Python.
Question: Why is "except:pass" a bad programming habit?
From time to time I see people commenting on * about the use of except: pass, and they all mention that it's a bad Python programming habit that should be avoided. But I wonder why? Sometimes I don't care about the error that occurs, but just want my program to continue. Like this:
try: something except: pass
Why is it bad to use except:pass so much? What is the reason behind this, is it because then I will let go of some errors that should be handled? Or is it because then I will catch all types of errors?
Best Answer:
As you might guess, there are indeed two downsides to doing this. First, because no exception type is specified, any type of error will be caught. Second, catching the error would simply let it pass instead of taking the necessary action.
My next explanation will probably be a bit long, so I will summarize the highlights below:
1. Do not capture any type of error. You must specify the types of errors you want to catch and write that only they are to be caught.
2. Do not try to simply perfume the error handling action. Unless there is a purpose for doing so, which is usually not very good.
So let's go a little deeper next:
Don't target arbitrary exceptions for catching
When you use an exception catching statement block somewhere in your code, you usually know that this place is likely to throw an exception, and you know what kind of problem is likely to occur and what kind of exception is likely to be thrown, and once the exception is thrown, you will catch it and get the program back on track. This means that you must be prepared for such an exception and be able to take steps to deal with it when it occurs.
For example, if you need the user to enter a number and use the int() function to convert the string to an integer, you would expect a ValueError to occur if the string is not a number. If an error does occur, you can get the program back on track by simply asking the user to retype, so catching the value error and prompting the user to retype is a reasonable strategy for dealing with it. As another example, if you want to read configuration information from a file that just happens to not exist. Then because it's a configuration file, if it didn't exist you'd return some default configuration options, so the file wouldn't be so necessary. In this example, catching the FileNotFoundError and returning the default configuration items would be a more appropriate handling strategy. As you can see from these two examples, we are waiting to catch specific errors and have a specific strategy for each.
However, if we were to catch all exceptions here, then those handling strategies prepared for specific exceptions would be invalidated by encountering abnormal types of exceptions, which would interrupt the normal program flow and make it impossible to recover.
Let's take the example of the configuration file. The normal strategy is that if we find that the file does not exist, we will use the default configuration item and may decide later whether to automatically save the current configuration item as a configuration file (in which case the file will definitely exist next time). Now let's assume that we catch an IsADirectoryError or PermissionError error, in which case we may not want the program to continue executing and we will still be able to use the default configuration parameters, but then we won't be able to save the file. It is also possible that the user wants to use a customized configuration item, so in that case the default configuration item cannot be used. So we may need to inform the user immediately and stop the program. It's also possible that we don't want to do so much in such a small piece of code, but rather let the application level part care about it, so we may also let the error float to the top level and let the top level business logic handle it.
existPython 2 idioms documentThe documentation also mentions a simple example: if there is a simple spelling error in our code that causes a program error. In this case since we catch all exceptions, we will catch NameErrors as well as SyntaxErrors. Both are common errors, and both are undesirable in our final code. But because we catch all exceptions, we can't tell the exact type of error when it occurs and we can't debug it.
But there are also dangerous exceptions that we are not prepared for, such as SystemError, which are so rare that we are not prepared for them at all; these exceptions usually require more complex handling operations, which may require us to stop what we are doing.
In any case, it is essentially impossible to implement handling of all exceptions through localized code, so you should target specific exceptions that have been prepared for. Some people have suggested that at least basic exceptions (Exception) should be explicitly excluded from exceptions such as SystemExit and KeyboardInterrupt, which are designed to terminate the application. However, I would say that this is still not clear enough, and I personally believe that there is only one place to catch only Exception or any type of exception, and that is a separate, application-level exception catcher whose sole task is to catch any unprepared exceptions that may occur. This way we can still retain information about the unexpected exception as a basis for further code expansion (if we can get the program to recover, of course), so that the next time we can explicitly specify the exception in the right place or write test cases to ensure that the error doesn't happen again (of course, this is only possible if we're prepared for the particular exception; an unprepared exception will still slip through the cracks). prepared exceptions will still slip through).
Don't do nothing in the exception handling logic
After explicitly catching a limited number of exceptions, there are many times when we really don't need to do anything special. In this case, except SomeSpecificException: pass is fine. In most cases, however, we still need some code related to error recovery, such as repeating attempted actions and setting default values.
Other situations are also taken into account, for example if our code has been structured in such a way that it must keep trying until it succeeds in order to continue, then doing nothing will suffice. Specifically, we need the user to enter a number, and since we know that the user may not do what we designed them to do, we'll put that part into a loop like this:
def askForNumber (): while True: try: return int(input('Please enter a number: ')) except ValueError: pass
Since we'll keep asking the user for input until no exceptions are thrown, we don't need to do anything else special in the except block in this case, and that's good enough. Of course, one could argue that you should at least let the user get some error messages to make it clear why they're being repeatedly asked for input here.
In other cases, the passing statement in the except block shows that we are not really prepared for the exception. Unless it's a simple exception (such as ValueError or TypeError) we should do something about it, for the obvious reason of avoiding simple passing; if there's nothing to do (and if you're really sure) then consider adding some explanatory comments here; otherwise, expand the except block to add some restorative code .
except: pass
The most intolerable thing is the combination of the two. This means that we voluntarily catch any exceptions (including those we are not prepared for) and ignore them. You should at least log the error in the log and raise it to terminate the program (I don't believe that your program will run normally with a MemoryError). Leaving these exceptions alone will keep the program on the wrong track and throw away critical error information, making the error less likely to be found, especially if you don't personally encounter it.
So the bottom line is to catch those specific exceptions that have been prepared for; the other exceptions that occur are either bugs waiting for you to fix them or ones you can't handle. It's OK to let certain specific exceptions go when there's really nothing else to do; otherwise it's just considered slacking or laziness if you do it. You really should be dealing with these exceptions.