Try-except-finally statement is used to manage error scenarios or failure. Example: trying to open file for reading, but file not found OR trying a/b, but b is 0!
Try block specifies operation that needs to be attempted if feasible.
Except block specifies what shall be done, if try attempt is failed in between.
Else block specifies what shall be done if try attempt is successful.
Finally block specifies what shall be done, irrespective of attempt succeeded or failed.
Exception object is raised from try block code, which indicates what failure occurred.
Say, some statement (operation or called function) got failed. One approach is to return error code, to indicate failure. In this case, program flow need to check error code. In such cases, try-except might not be required.
There is one more facility by programming language called "throwing exception" (in python its called "raising exception"). In this case, program execution / control flow halts there and it is diverted to exception handling code in except block (generally provided by user). In such cases, try-except block needs to be provided.
try: statements except (Exception1, Exception2, Exception3) as my_e1: statements except Exception4 as my_e1: statements except: statements else: statements finally: statementsHere, my_e1 is variable name specified by user.
Except, Else and Finally are optional, but only certain combinations are valid as below:
# Example, Python try-except-finally try: a = 1 b = 0 c = a / b except Exception as e1: type(e1) print(e1) finally: print("Some cleaning code here.")
Exception is class in python. During try clause execution, Exception object is used to divert control flow from try to except. It can carry additional information about failure.
How exception got raised? Say, you are calling some function. Internally, it might be doing various operations and interacting with system (memory/file/network). So, exception might have been raised from your called function.
In other cases, you can raise exception (for your own need) using raise statement.
try: statements raise Exception("Some message") statements except Exception: statements
# Example, Python raise exception a = 1 try: b = 5 value = a - b if value < 0: raise Exception(value, "Value become negative") print("With a=1, b=5, this will not be printed.") except Exception as e1: type(e1) print(e1) # prints arguments of e1 finally: b = 0
What can be raised as Exception
You can create your own classes derived from Exception class and raise it.
You can raise built-in exceptions (example, TypeError, RuntimeError. Pls refer Exception hierarchy for list of built-in exceptions.
Any class derived from BaseException can be raised as Exception.
Can exception get raised even without Try-Except block?
Yes, even if try-except block is not provided, built-in exceptions can get raised from any of called function. Hence, it is good practice to study, if any of called function raises any exception.
Within Try block
In normal flow (i.e. without failure), all code inside try block gets executed. Since no exception is raised, code inside except block would not be executed. If finally block is provided, it would be executed.
In error flow (i.e. some failure occurred in try block), code till error generation point gets executed (i.e. till exception raising). Steps below exception raising point but within try block would not be executed. Rather, control flow would jump to except block.
Within Except block
Control will reach to except block only if some exception is raised. It also carries object raised as exception. It compares type of raised exception with except block's exception name. Since there can be multiple except clauses, it tries to match from top to bottom one by one, and, it executes matched except block's code. If finally is provided, it would be executed after except block. And control flow would continue after try-except-finally block.
It would not execute more than one except block's code, i.e. matching exercise stops on first matching. Hence good approach is to specify precises exception at top and BaseException at last (because, this would surely be matched.). If BaseException is kept on top (i.e. as first within except), it would always match first BaseException, and remaining except blocks within same try-except would never be useful.
In case raised exception is not matching with any of except blocks, current function's execution halts, and control flow would jump to outer try-except (i.e. caller of current function). Control flow would reach to except block of outer try-except. Again matching exercise continues and same approach continues till main program.
In case, it does not match at any level (example, try-except is not written in code), program execution halts.
What code should be written in except clause. General approach is to specify fallback mechanism for the try attempt. Example, file opening failed in try clause because file not existing, fallback approach is to create empty file in except clause.
Multiple exceptions can be specified in tuple. Here raised exception would be matched with tuple element, same except block would be executed for any of the match. It is helpful to manage common code for specific Exceptions.
# Example, Python handling multiple exceptions a = 1 try: b = 1 my_list = [0, 1] #my_list.extend(b) # raises TypeError value = a - b b = a / c # raises NameError, as c is not defined, but used. b = 0 value = a / b # raises ZeroDivisionError except (TypeError, NameError) as e1: type(e1) print(e1) # prints arguments of e1 except ZeroDivisionError as e1: print(e1) finally: value = 1
Catch All Exceptions
It is feasible to process specific exceptions with names, and then, to process rest of the exceptions without name.
# Example, Python catch all exceptions a = 1 try: b = 1 my_list = [0, 1] #my_list.extend(b) # raises TypeError value = a - b #b = a / c # raises NameError, as c is not defined, but used. b = 0 value = a / b # raises ZeroDivisionError except (TypeError, NameError) as e1: type(e1) print(e1) # prints arguments of e1 except: print("Unknown exception occurred.") finally: value = 1
It is good practice to specify all required information when exception is raised. If message is for end user, simple message is helpful. If message is for programmer, additional details to track root cause is useful.
# Example, Python print exception a = 1 try: b = 5 value = a - b if value < 0: raise Exception("Calculation failed with negative value. " + "a:" + str(a) + ", b:" + str(b), value) except Exception as e1: print(str(type(e1)) + ", " + str(e1) ) finally: b = 0
Else clause specifies steps to be followed ONLY in success path after try. Please refer below example for value of a=1 and a=10.
In failure path, exception gets raised and else part would not be executed. In success path, else part would be executed. Finally part would be executed in both cases.
Else clause skips execution, if try block has break, continue or return. (Note: Finally block does not skip execution even if try block has break, continue or return.)
Else clause is optional, but it can not exist without except clause. i.e. Try-else without except is not valid. Try-except-else is valid.
# Example, Python try-except-else-finally a = 1 try: b = 5 value = a - b if value < 0: raise Exception("Calculation failed with negative value. " + "a:" + str(a) + ", b:" + str(b), value) except Exception as e1: print(str(type(e1)) + ", " + str(e1) ) else: value = value * 10 finally: b = 0 print("a:" + str(a) + ", b:" + str(b) + ", value:" + str(value))
User defined exceptions are derived from Exception class. General approach is to provide message as instance member when custom exceptions are defined.
You can create structured custom exceptions, each one with additional instance members as per need. Example, network exceptions can have additional parameters related to connection type OR database exceptions can have additional parameters related to query.
# Example, Python custom exception class MyException(Exception): def __init__(self, message): self.message = message a = 1 try: b = 5 value = a - b if value < 0: raise MyException("Calculation failed with negative value. " + "a:" + str(a) + ", b:" + str(b) ) except MyException as e1: print(e1.message)
Exception domino effect
Once exception raised within try, control shifts to except. Imagine that some other failure occurred in except block! This would raise another exception! In case, one more try-except provided within except block, it can be managed there, otherwise control flow would be transferred to caller.
In all cases, finally clause would be executed before control transfer.
Finally part gets executed in all below cases:
- Try part successfully executed.
- Try clause has break, continue, return statement and no failure occurred. Finally part gets executed before control transfer due to break, continue, return.
- Try clause raises Exception and it is matched with any of except.
- Try clause raises Exception and it is not matching to any of the except clause.
- Some other exception raised in except.