Exception Handing in Python

Exception : An exception if an error occurs during the execution of the program, which disrupts the normal flow of the program's instructions.

Example:
Internet Error
FileNotFound Error
Division Error
Value Error

Exception Handling: An Exception handling does not mean that repairing an exception, we have to define an alternative way to continue the program normally. The method of defining an alternative way is nothing but an exception handling.

Example:

try:
    Read data from Remote file locating at Landon
    
except FileNotFoundError:
       use a local file and continue rest of the program normally    

The Main Objective of the Exception Handling:

  • It is highly recommended to perform exception handling.
  • The main objective is to terminate the program in a gracefull way/ normal termination of the application so that we should not miss anything or should not block our resources.

There are two types of Exceptions Occurs during the Execution of the program

Syntax Error in Python Exception Handling

The Errors which occur due to invalid syntax in the program are called syntax Errors; the programmer is responsible for correcting those errors. Once all the syntax errors are corrected, then only the program execution will start.

Example:

value=10
##missed colon(:) in the below line 
if value==10
   print("Hello")

##The output is:
 File "C:UsersUserAnaconda3libsite-packagesspyder_kernelscustomizespydercustomize.py", line 110, in execfile
    exec(compile(f.read(), filename, 'exec'), namespace)

  File "C:/Users/User/.spyder-py3/temp.py", line 2
    if value==10
            ^
SyntaxError: invalid syntax

Mutability in Python

Runtime Error in Python Exception Handling

Runtime errors are also known as exceptions. While executing the program, at the runtime if something goes wrong because of end-user input or due to memory problem or programming logic, etc..then we will encounter runtime errors.

The Exception handling concept is applicable only for Runtime errors and not for Syntax errors.

Example:

Example 1:
print(10/0)
##The output is:
  File "C:/Users/User/.spyder-py3/temp.py", line 1, in <module>
    print(10/0)

ZeroDivisionError: division by zero

Example 2:
Info=int(input("Enter Number:"))
print(Info)
##The output is:
Enter Number:Ten
Traceback (most recent call last):

  File "<ipython-input-5-5ba32625e270>", line 1, in <module>
    runfile('C:/Users/User/.spyder-py3/temp.py', wdir='C:/Users/User/.spyder-py3')

  File "C:UsersUserAnaconda3libsite-packagesspyder_kernelscustomizespydercustomize.py", line 786, in runfile
    execfile(filename, namespace)

  File "C:UsersUserAnaconda3libsite-packagesspyder_kernelscustomizespydercustomize.py", line 110, in execfile
    exec(compile(f.read(), filename, 'exec'), namespace)

  File "C:/Users/User/.spyder-py3/temp.py", line 1, in <module>
    Info=int(input("Enter Number:"))

ValueError: invalid literal for int() with base 10: 'Ten'

Python Immutability

Default Exception Handling in Python

Every exception in python is an object, and for every exception type, the corresponding class is available.

Whenever an exception occurs, the python virtual machine will create the corresponding exception object and will check for the handling code, if the corresponding handling code is available, then the code will be run normally.

If the handling code is not available, then the python interpreter terminates the program abnormally and prints corresponding exception information to the console, and the rest of the program won't be executed.

To prevent this abnormal termination, we should handle exceptions explicitly, of course, by using try-except blocks.

Pythons Exception Hierarchy

Every exception in python is class, all the exception classes are child classes of a base class exception either directly or indirectly, and hence BaseException acts as a root for python exception hierarchy.

Being a programmer most of the time, we need to concentrate/handle the exception and its child classes.

The Image of Pythons Exception Hierarchy

python-exception-heirarchi

Python Bytes and Bytearray

Customized Exception Handling in python

The main objective of exception handling is to perform graceful termination of an application, and hence it is highly recommended to handle exceptions.

The code which may raise an exception is called risky code, and we have to take that code inside the try block and the corresponding handling code we have to take inside the except block.

So if any exceptions arise inside the try block immediately, the exception block will execute, and the alternative code will execute, and the program terminates normally.

The syntax for the customized exception handling is:

try:
 Risky Code
except XXX:
 Handling code/Alternative Code

Example: Without Try Except Block

In the below code, the exception is raised in the second statement, and hence there is no except block which provides the alternate code to terminate the program normally, then python will throw a ZeroDivisionError, and the program is terminated abnormally.

print("statement-1")
print(10/0)
print("statement-3")
##The output is:

  File "C:/Users/User/.spyder-py3/temp.py", line 2, in <module>
    print(10/0)

ZeroDivisionError: division by zero

Example: With Try-Except Block

In the below program an except block is present and hence, once the error occurs at the second statement, then the control will automatically go to except block which executes the alternate code, and the program will terminate normally by executing the third statement/ rest of the program.

print("statement-1")
try:
    print(10/0)
except ZeroDivisionError:
    print(10/2)
print("statement-3")

The output is:

running-program-with-try-except-block

None Datatype

Control Flow in Try-Except Block in Python's Exception Handling

Within the try block if anywhere an exception raised then the rest of the try block won't be executed even though if we handled that exception, hence inside the try block we have to take only risky code and the length of the try block should be as less as possible.

try:
    print("statement-1")
    print(10/0)
    print("Statement-3")
    
except ZeroDivisionError:
    print("Statement-4")
print("statement-5")    

The output is: Even After providing the except block, the third statement in the try block won't be executed.

python-flow-control-case-one

In addition to the try block, there may be a chance of raising exceptions inside the except and finally blocks also.

In any statement which is not a part of the try block and raising an exception, then it is always an abnormal termination.

If you want to print the exception information inside the except blocks, like Name of the exception, Type of the exception or Description of the exception then you can enter the information before the handling code.

An Exception object got created with a reference variable, and the reference variable can be anything.

The syntax to print Exception Information to the console is:

try:
    print("statement-1")
    print("statement-2")  #Risky Code
except NameOfTheError as ReferenceVariable: #as is the keyword we have to use compulsory
    print("Statement-3")
print("statement-4")    

Example:

try:
    print(10/0)
except ZeroDivisionError as msg:
    print("Exception Type:", type(msg))

The output is:

printing-exception-information-into-console

We can print the type of the exception by using the corresponding class object as follow:

try:
    print(10/0)
except ZeroDivisionError as msg:
    print("Exception Type:", type(msg))
    print("Exception Type:",msg.__class__) ##printing the corresponding class object

The output is:

printing-the-corresponding-class-object

If you want to print only the exception class Name then:

try:
    print(10/0)
except ZeroDivisionError as msg:
    print("Exception Type:", type(msg))
    print("Exception Type:",msg.__class__)
##Printing exception class name
    print("Exception Type:", msg.__class__.__name__) 

The output is:

printing-exception-class-name

If you want to print the description of the exception, then :

try:
    print(10/0)
except ZeroDivisionError as msg:
##Printing the description of exception
    print("Description of Exception:", msg)

The output is:

printing-the-description-of-exception

The following example demonstrates handling both parent class exception and child class exceptions by using BaseException as the class object.

try:
    first_value=int(input("Enter First Number:"))
    second_value=int(input("Enter Second Number:"))
    print("The Result:",first_value/second_value)
except BaseException as result:
    print("Exception Type:", type(result))
    print("Exception Type:", type.__class__)
    print("Exception Class Name:", result.__class__.__name__)
    print("Description of Exception:", result)    

The output of the parent class:

output-of-parent-class

The output of handling child class exception:

handling-child-class-exception

Python List Datatypes

Try Block with Multiple Exception Block in python

The Way of Handling an Exception is varied from exception to exception, hence for every possible exception type, We have to take separate except block. The exception block will execute until to get the matched exception block.

Try block with multiple exceptions is possible and recommended to use.

The syntax is:

try:
   print("statement-1")
except NameOfError:
  perform alternative operation
except NameOfError:
  use local file instead of remote file

If the try block with multiple blocks is available, based on the available exception the corresponding except block will be executed.

The following example will demonstrate the try block with multiple except block

try:
    value_one=int(input("Enter First Number:"))
    value_second=int(input("Enter Second Number:"))
    print("The Result:",value_one/value_second)
except ZeroDivisionError:
    print("Cannot Divide with Zero")
except ValueError:
    print("Please Provide int values only")    

The output is: When we give 10 as the first value and second value as 5

first-output-of-multiple-except-bloack

The output, when we give the first value as 10 and second value as 0

second-output-multiple-exception

The output, when we provide the first value as 10 and second value as Two

third-output-of-multiple-exception

If try block with multiple except blocks is available, then the order of these except block is important, and the python virtual machine will always consider from top to bottom until the matched except block is available.

The following example demonstrates how the except block will execute from top to bottom

try:
    print(20/0)
except ZeroDivisionError:    
    print("ZeroDivisionError")
except ArithmeticError:
    print("ArithmeticError")
    

The output is:

zero-division-error

The same code, if write the arithmetic error first and next to zero division error then, the arithmetic error exception will execute because the zero division error is the child class of arithmetic error.

try:
    print(20/0)
except ArithmeticError:
    print("ArithmeticError")
except ZeroDivisionError:    
    print("ZeroDivisionError")    
    

The output is:

arithmetic-error

Nested List in Python

Single Exception block that can handle multiple exceptions in python

If a handling code is the same for multiple exceptions then instead of taking different except blocks, we can take single except block that can handle all the exceptions.

Syntax is:

except(Exception1, Exception2,...):
except(Exception1, Exception2,...) as msg:

The parenthesis is mandatory and this group of exceptions internally considered as Tuple.

The following example demonstrates the single exception block handling multiple exceptions

try:
    value_one=int(input("Enter First Number:"))
    value_second=int(input("Enter Second Number:"))
    print("The Result:",value_one/value_second)
except (ZeroDivisionError, ValueError) as msg:
    print("The Raised Exception:", msg.__class__.__name__)
    print("Description of Exception:", msg)   
    print("Please provide valid inputs only...")

The output is: When the input is 10 and 5

single-exception-block-handling-mutliple-exception

The output is: When the input is 10 and 5, then the zerodivisionerror exception will execute

zero-division-error-occur-single-exception-block-handling-multiple-exception

The output is: When the input is 10 and Two, then the Valueerror exception will execute

valueerror-single-exception-block-handle-multiple-exception

Tuple Datatype

Default Except Block in python

We can use default except block to handle any exceptions; In the default except block generally, we can print exception information to the console.

The syntax is:

except: 
          statements

The following example demonstrates the default except block

try:
    value_one=int(input("Enter First Number:"))
    value_second=int(input("Enter Second Number:"))
    print("The Result:",value_one/value_second)
except ZeroDivisionError:
    print("ZeroDivisionError:cannot divide wih zero")
except:
    print("Default Except:please provide valid input only")

The output is: The ZeroDivisionError block will execute when input is 30 and 0

default-exceptions-zerodivision-error

The output is: The Except block will execute when the zerodivisionerror does not match.

deafult-exception-except-block-execution

In the above code if we remove zerodivisionerror block also the except block can handle it

try:
    value_one=int(input("Enter First Number:"))
    value_second=int(input("Enter Second Number:"))
    print("The Result:",value_one/value_second)
except:
    print("Default Except:please provide valid input only")

The output is:

deafult-exception-execution

If default except block is defined then compulsory it must be a last except block; otherwise, we will get a syntax error.
This restriction is applicable only for Default except block and not for the normal Except blocks

Various possible combinations of except block

  • except ZeroDivisionError:
  • except (ZeroDivisionError):
  • except ZeroDivisionError as msg:
  • except (ZeroDivisionError) as msg
  • except (ZeroDivisionError, ValueError):
  • except (ZeroDivisionError, ValueError) as msg:
  • except:

If except block is defined for only one exception, then parenthesis is optional, and if except block is defined for more than one except block, then parenthesis is mandatory.

If we use parenthesis then as keyword must be outside the parenthesis.

Invalid Combinations of except Block

  • except (ZeroDivisionError as msg)
  • except ZeroDivisionError, ValueError:
  • except (ZeroDivisionError, ValueError as msg):

Finally, Block in python

The finally keyword is used in a try...except blocks. It defines a block of code to run when the try...except...else block is final.

It is not recommended to place a cleanup code inside the try block because there is no guarantee for the execution of every statement inside the try block.

And also, it is not recommended to place a cleanup code inside the except block, because if there is no exception then except block won't be executed.

We required someplace to define a cleanup code which executes irrespective of whether the exception raised or not raised and whether the exception is handled or not handled, the best place is nothing but a finally block.

The main purpose of the finally code is to maintain a cleanup code.

The Syntax is:

try:
    Risky code:
except:
       Handling code
finally:
      Cleanup code

Case 1: If No Exception

try:
    print("try")
except ZeroDivisionError:
    print("Except")
finally:
    print("finally")

If there is no exception then try and finally block will execute

if-there-is-no-exception-then-try-finally-blocks-will-execute

Case 2: If the exception raised and handled

try:
    print("try")
    print(10/0)
except ZeroDivisionError:
    print("Except")
finally:
    print("finally")

If an exception got raised and handled by exception block, then try, except and finally blocks got executed.

if-there-is-an-exception-then-try-except-finally-block-executes

Case 3:If exception raised but not handled

try:
    print("try")
    print(10/0)
except ValueError:
    print("Except")
finally:
    print("finally")

If the exception raised but not handled then first try and finally block will execute, and then zeroDivisionError will execute.

If-exception-raised-but-not-handled

OS.__exit(0) vs Finally Block in python

There is only one situation where finally block won't be executed, i.e., whenever we are using OS._exit(0).

Whenever we are using the OS_exit(0), then the python virtual machine itself will be shut down and in this particular case finally, the block won't be executed.

OS._exit(0)

Here Zero represents the status Code.

Zero means normal termination.

Non-zero means abnormal termination

The python virtual machine internally uses this status code.

Whether it is zero or non zero, there is no difference in the result of the program.

The following example demonstrates the OS._exit(0)

import os
try:
    print("try")
    os._exit(0)
except ValueError:
    print("except")
finally:
    print("finally")

The output is:
try

Difference between Finally Block and Destructor

Finally Block Destructor
Finally block is meant for cleanup activities related to the try block, i.e., whatever the resources we opened as part of try block will be cleaned inside the finally block.

Destructor is meant for cleanup activities related to the object; whatever the resources related to the object should be deallocated inside the destructor which will be executed before destroying the object.

Control Flow in try-except-finally blocks

The following cases demonstrate the control flow in try-except-finally blocks

Case 1: If there is no exception

try:
    print(statement-1)
    print(statement-2)
    print(statement-3)
except:
    print(statement-4)
finally:
    print(statement-5)
print(statement-6)   

If there is no exception occur, then, try block will execute and finally block will also execute, excluding the except and then the remaining code will execute normally.

The output will be : statement-1,2,3,5,6 and normal termination

Case 2: If an exception raised at the statement-2 and corresponding except block matched, then the control automatically goes to except block and then finally block will execute, also remaining blocks will execute normally.

The output will be: statement-1,4,5,6 and normal termination.

Case 3:If an exception raised at the statement-2 and the corresponding except block not matched; then the finally block will execute, and the abnormal termination will happen.

The output will be: statement-1, normal termination.

Case 4: If an exception raised at the statement-4 if an exception raised at the exception block, then finally block will execute and the abnormal termination will happen.

The output will be: statement-5 and abnormal termination.

Case 5:If an exception raised at statements 5 or 6 then it is always abnormal termination.

Nested try-except-finally Blocks

We can take try-except-finally blocks inside the try or except or finally. Hence nesting of try-except-finally blocks is possible.

The general risky code we have to take inside the outer try block and too much risky code we have to take inside the inner try block.

Inside the inner try block if an exception is raised, then the inner except block is responsible to handle it; if it is unable to handle, then the outer except block is responsible for handling it.

The following example demonstrates the nested try-except-finally block

try:
    print("outer try block")
    try:
        print("Inner try block")
    except ZeroDivisionError:
        print("Inner except block")
    finally:
        print("Inner finally block")
except:
    print("outer except block")
finally:
    print("outer finally block")        

There is no exception in the above program and hence the inner except and outer except block won't be executed, except that all the blocks will execute.

The output is:

nested-try-except-finally-block

If an exception is raised in the inner try block and the inner except block will match then the inner exception block, and inner finally block will execute.

And the exception is handled by the inner exception itself, and hence the outer exception block won't execute, but the outer finally block will execute.

try:
    print("outer try block")
    try:
        print("Inner try block")
        print(10/0)
    except ZeroDivisionError:
        print("Inner except block")
    finally:
        print("Inner finally block")
except:
    print("outer except block")
finally:
    print("outer finally block")     

The output is:

nested-try-except-finally-block-an-exception-is-raised-in-the-inner-try-block

In the below example, an exception is raised in the inner try block but the corresponding except block not matched, and hence the inner finally block will execute and then the outer except block will execute and then the outer finally block.

try:
    print("outer try block")
    try:
        print("Inner try block")
        print(10/0)
    except ValueError:
        print("Inner except block")
    finally:
        print("Inner finally block")
except:
    print("outer except block")
finally:
    print("outer finally block")   

The output is:

nested-try-except-block-exception-is-raised-in-the-innertryblock%20-but-the-corresponding-exceptblock-notmatched
If an exception is raised in the outer try block, then the corresponding except block will be outer except block, and hence the outer except block and the outer finally will be executed.

try:
    print("outer try block")
    print(10/0)
    try:
        print("Inner try block")
        
    except ValueError:
        print("Inner except block")
    finally:
        print("Inner finally block")
except:
    print("outer except block")
finally:
    print("outer finally block")   

The output is:

nested-try-except-finaly-if-an-exception-raised-in-the-outer-try-block

else Block with try-except-finally in python's exception Handling

if-else==>if condition is false then only else will be executed
for-else==>If loop executed without a break, then only else block will execute.
while-else==>if loop executed without a break then only else will execute.

The following example demonstrates, if the condition is false, then only the else part will execute.

value=10
if value>10:
    print("value is greater than 10")
else:
    print("value is less than 10")

The output is:

else-block-execute-only-when-if-statement-fails

The following example demonstrates the loops with else block

for info in range(10):
    print("The Current Item:",info)
else:
    print("congratulations, all item processed successfully")

The output is:

loop-with-else

The following example demonstrates While executing a loop if the break statement is encountered, then the else part is not going to execute.

for info in range(10):
    if info>5:
        break
    print("The Current Item:",info)
else:
    print("congratulations, all item processed successfully")

The output is:

the-output-of-else-with-break-statement

If we use else block with try-except-finally, if there is no exception in a try block, then only the else block will execute

The Syntax is:

try:
    Risky code
except:
    Handling code
    will be executed if an exception inside try
else:
    will be executed if there is no exception inside try
finally:
    cleanup code
    will be executed whether exception raised or not raised and handled or not handled    

The following example demonstrates the; when there is no exception in the try block then else part will execute

try:
   print("try")
except:
       print("Except")
else:
     print("Else")
finally:
      print("Finally")

The output is:

else-block-execution-ehrn-there-is no-exception-in-the-try-block

The following example demonstrates the execution of else block with try-except-finally when an exception occurs in the try block then the else part will not execute.

try:
   print("try")
   print(10/0)
except:
       print("Except")
else:
     print("Else")
finally:
      print("Finally")

The output is:

else-block-with-try-except-finally

The following example demonstrates without except block the else part is not valid and the python will throw a syntax error

try:
   print("try")
else:
     print("Else")
finally:
      print("Finally")

The output is:

without-except-block-the-else-block-is-invalid

There is no chance of executing both except block and else simultaneously If we want to use the else block then compulsory the except block should be there

The following example demonstrates the else block with try-except-finally block

file=None
try:
    file=open("info.txt","r")
except:
    print("Some problem while locating and opening the file")
else:
     print("File opened successfully")
     print("The data present in the file is:")
     print("#",*30)
     print(file.read())
finally:
    if file is not None:
        file.close()

The output is: If the file is available in my system then

File opened successfully
The data present in the file is:
#############################
Line-1 of abc.txt file
Line-2 of abc.txt file
Line-3 of abc.txt file
Line-4 of abc.txt file
Line-5 of abc.txt file

The output is: If the file is not present in the system.

if-the-file-is-not-present-in-the-system

The Various Possible Combinations of try-except-else-finally Block

Whenever we are writing try block, compulsory we should write except or finally block, Because the try block without except or finally is invalid.

Whenever we are writing except block, compulsory try block should be there, because the except without try block is always invalid.

Whenever we are writing finally block compulsory try block should be there, because the finally block without try is always invalid.

Whenever we are writing else block compulsory except block should be there, because else without except is always invalid.

We can write multiple except blocks for the same try block with, but we cannot write various else block and finally block.

The try-except-else-finally order is important.

We can write try-except-else-finally block inside try-except-else-finally block

The following table explains the valid syntax for the try-except-else-finally block.

Syntax

Status

try:

print("try")

Invalid

except:

print("Hello")

Invalid

else:

print("Hello")

Invalid

finally:

print("Hello")

Invalid

try:

print("try")

except:

print("except")

Valid

try:

print("try")

finally:

print("finally")

Valid

try:

print("try")

except:

print("except")

else:

print("else")

Valid

try:

print("try")

else:

print("else")

Invalid

try:

print("try")

else:

print("else")

finally:

print("finally")

Invalid

try:

print("try")

except xxxx:

print("except-1")

except yyyy:

print("except-2")

Valid

try:

print("try")

except:

print("except-1")

else:

print("else")

else:

print("else")

Invalid

try:

print("try")

except:

print("except-1")

finally:

print("finally")

finally:

print("finally")

Invalid

try:

print("try")

print("Hello")

except:

Invalid

try:

print("try")

except:

print("except")

print("Hello")

finally:

print("finally")

Invalid

try:

print("try")

except:

print("except")

print("Hello")

else: print("else")

Invalid

try:

print("try")

except:

print("except")

try:

print("try")

except:

print("except")

Valid

try:

print("try")

except:

print("except")

try:

print("try")

finally:

print("finally")

Valid

try:

print("try")

except:

print("except")

if 10>20

print("if")

else:

print("esle")

Valid

try:

print("try")

try:

print("inner try")

except:

print("inner except block") finally:

print("inner finally block") except:

print("except")

Valid

try:

print("try")

except:

print("except")

try:

print("inner try")

except:

print("inner except block") finally:

print("inner finally block")

Valid

try:

print("try")

except:

print("except")

finally:

try:

print("inner try")

except:

print("inner except block") finally:

print("inner finally block")

Valid

try:

print("try")

except:

print("except")

try:

print("try")

else:

print("else")

Invalid

try:

print("try")

try:

print("inner try")

except:

print("except")

Invalid

try:

print("try")

else:

print("else")

except:

print("except")

finally:

print("finally")

Invalid

Types of Exceptions in python Exception Handling

There are two types of exceptions in python

  • Predefined Exceptions
  • User-Defined Exceptions

Predefined Exceptions:

The predefined exceptions will raise automatically by the python virtual machine whenever a particular event occurs.

The predefined exceptions are also known as inbuilt exceptions or PVM Exceptions.

Example 1: Whenever we are trying to perform a division by zero, automatically python will raise

ZeroDivisionError.

print(10/0)  #ZeroDivisionError

Example 2: Whenever we are trying to convert str type into int type and the string does not contain the int valve, then we will get ValueError Automatically.

data=int("Ten") ##ValueError

User-Defined Exceptions:

Sometimes we have to define and raise exceptions explicitly to indicate that something goes wrong, such types of exceptions are called User Defined Exceptions or Customized Exceptions. The user-defined exceptions are also known as Customized Exceptions or Programmatic Exceptions.

The programmer is responsible for defining these exceptions and Python not having any idea about these. Hence we have to raise explicitly based on our requirement by using the "raise" keyword.

Example:

InsufficientFundsException
InvalidPinException
TooYoungException
TooOldException

How to Define and Raise Customized Exceptions:

Every exception in python is a class, and it should be a child class of BaseException.

Example:

class NameofException(PredefinedException):
     def __init__(self,msg):
           self.msg=msg

Example:

class TooYongException(Exception):
      def __init__(self,msg):
            self.msg=msg

The TooYoungException is our exception class name, and it is the child class Exception, and we can raise an exception by using the raise keyword.

The Syntax is:

raise TooYoungException("Massage")

The following example demonstrates the customized exception

class TooYoungException(Exception):
    def __init__(self,msg):
        self.msg=msg
        
class TooOldException(Exception):
    def __init__(self,msg):
        self.msg=msg
        
age=int(input("Enter Age:"))
if age>17:
    raise TooYoungException("Please Wait some more time, definitely you will get chance to vote")
elif age>18:
   raise TooOldException("Now, you are eligible to vote")
else:
    print("As per the gov rules the age should 18 to participate in voting")        

The output is: When the input is 12 then

below-eighteen-to-vote

The output is: When the input is 25

when-the-age-ismore-than-18

Comment / Suggestion Section
Point our Mistakes and Post Your Suggestions