Error messages in python

How do I print the error/exception in the except: block? try: ... except: print(exception)

One has pretty much control on which information from the traceback to be displayed/logged when catching exceptions.

The code

with open("not_existing_file.txt", 'r') as text:
    pass

would produce the following traceback:

Traceback (most recent call last):
  File "exception_checks.py", line 19, in <module>
    with open("not_existing_file.txt", 'r') as text:
FileNotFoundError: [Errno 2] No such file or directory: 'not_existing_file.txt'

Print/Log the full traceback

As others already mentioned, you can catch the whole traceback by using the traceback module:

import traceback
try:
    with open("not_existing_file.txt", 'r') as text:
        pass
except Exception as exception:
    traceback.print_exc()

This will produce the following output:

Traceback (most recent call last):
  File "exception_checks.py", line 19, in <module>
    with open("not_existing_file.txt", 'r') as text:
FileNotFoundError: [Errno 2] No such file or directory: 'not_existing_file.txt'

You can achieve the same by using logging:

try:
    with open("not_existing_file.txt", 'r') as text:
        pass
except Exception as exception:
    logger.error(exception, exc_info=True)

Output:

__main__: 2020-05-27 12:10:47-ERROR- [Errno 2] No such file or directory: 'not_existing_file.txt'
Traceback (most recent call last):
  File "exception_checks.py", line 27, in <module>
    with open("not_existing_file.txt", 'r') as text:
FileNotFoundError: [Errno 2] No such file or directory: 'not_existing_file.txt'

Print/log error name/message only

You might not be interested in the whole traceback, but only in the most important information, such as Exception name and Exception message, use:

try:
    with open("not_existing_file.txt", 'r') as text:
        pass
except Exception as exception:
    print("Exception: {}".format(type(exception).__name__))
    print("Exception message: {}".format(exception))

Output:

Exception: FileNotFoundError
Exception message: [Errno 2] No such file or directory: 'not_existing_file.txt'

Watch Now This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding: Raising and Handling Python Exceptions

A Python program terminates as soon as it encounters an error. In Python, an error can be a syntax error or an exception. In this article, you will see what an exception is and how it differs from a syntax error. After that, you will learn about raising exceptions and making assertions. Then, you’ll finish with a demonstration of the try and except block.

An introduction to exceptions in Python

Exceptions versus Syntax Errors

Syntax errors occur when the parser detects an incorrect statement. Observe the following example:

>>> print( 0 / 0 ))
  File "<stdin>", line 1
    print( 0 / 0 ))
                  ^
SyntaxError: invalid syntax

The arrow indicates where the parser ran into the syntax error. In this example, there was one bracket too many. Remove it and run your code again:

>>> print( 0 / 0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero

This time, you ran into an exception error. This type of error occurs whenever syntactically correct Python code results in an error. The last line of the message indicated what type of exception error you ran into.

Instead of showing the message exception error, Python details what type of exception error was encountered. In this case, it was a ZeroDivisionError. Python comes with various built-in exceptions as well as the possibility to create self-defined exceptions.

Raising an Exception

We can use raise to throw an exception if a condition occurs. The statement can be complemented with a custom exception.

Illustration of  raise statement usage

If you want to throw an error when a certain condition occurs using raise, you could go about it like this:

x = 10
if x > 5:
    raise Exception('x should not exceed 5. The value of x was: {}'.format(x))

When you run this code, the output will be the following:

Traceback (most recent call last):
  File "<input>", line 4, in <module>
Exception: x should not exceed 5. The value of x was: 10

The program comes to a halt and displays our exception to screen, offering clues about what went wrong.

The AssertionError Exception

Instead of waiting for a program to crash midway, you can also start by making an assertion in Python. We assert that a certain condition is met. If this condition turns out to be True, then that is excellent! The program can continue. If the condition turns out to be False, you can have the program throw an AssertionError exception.

Python assert statement

Have a look at the following example, where it is asserted that the code will be executed on a Linux system:

import sys
assert ('linux' in sys.platform), "This code runs on Linux only."

If you run this code on a Linux machine, the assertion passes. If you were to run this code on a Windows machine, the outcome of the assertion would be False and the result would be the following:

Traceback (most recent call last):
  File "<input>", line 2, in <module>
AssertionError: This code runs on Linux only.

In this example, throwing an AssertionError exception is the last thing that the program will do. The program will come to halt and will not continue. What if that is not what you want?

The try and except Block: Handling Exceptions

The try and except block in Python is used to catch and handle exceptions. Python executes code following the try statement as a “normal” part of the program. The code that follows the except statement is the program’s response to any exceptions in the preceding try clause.

Diagram showing try and except statements

As you saw earlier, when syntactically correct code runs into an error, Python will throw an exception error. This exception error will crash the program if it is unhandled. The except clause determines how your program responds to exceptions.

The following function can help you understand the try and except block:

def linux_interaction():
    assert ('linux' in sys.platform), "Function can only run on Linux systems."
    print('Doing something.')

The linux_interaction() can only run on a Linux system. The assert in this function will throw an AssertionError exception if you call it on an operating system other then Linux.

You can give the function a try using the following code:

try:
    linux_interaction()
except:
    pass

The way you handled the error here is by handing out a pass. If you were to run this code on a Windows machine, you would get the following output:

You got nothing. The good thing here is that the program did not crash. But it would be nice to see if some type of exception occurred whenever you ran your code. To this end, you can change the pass into something that would generate an informative message, like so:

try:
    linux_interaction()
except:
    print('Linux function was not executed')

Execute this code on a Windows machine:

Linux function was not executed

When an exception occurs in a program running this function, the program will continue as well as inform you about the fact that the function call was not successful.

What you did not get to see was the type of error that was thrown as a result of the function call. In order to see exactly what went wrong, you would need to catch the error that the function threw.

The following code is an example where you capture the AssertionError and output that message to screen:

try:
    linux_interaction()
except AssertionError as error:
    print(error)
    print('The linux_interaction() function was not executed')

Running this function on a Windows machine outputs the following:

Function can only run on Linux systems.
The linux_interaction() function was not executed

The first message is the AssertionError, informing you that the function can only be executed on a Linux machine. The second message tells you which function was not executed.

In the previous example, you called a function that you wrote yourself. When you executed the function, you caught the AssertionError exception and printed it to screen.

Here’s another example where you open a file and use a built-in exception:

try:
    with open('file.log') as file:
        read_data = file.read()
except:
    print('Could not open file.log')

If file.log does not exist, this block of code will output the following:

This is an informative message, and our program will still continue to run. In the Python docs, you can see that there are a lot of built-in exceptions that you can use here. One exception described on that page is the following:

Exception FileNotFoundError

Raised when a file or directory is requested but doesn’t exist. Corresponds to errno ENOENT.

To catch this type of exception and print it to screen, you could use the following code:

try:
    with open('file.log') as file:
        read_data = file.read()
except FileNotFoundError as fnf_error:
    print(fnf_error)

In this case, if file.log does not exist, the output will be the following:

[Errno 2] No such file or directory: 'file.log'

You can have more than one function call in your try clause and anticipate catching various exceptions. A thing to note here is that the code in the try clause will stop as soon as an exception is encountered.

Look at the following code. Here, you first call the linux_interaction() function and then try to open a file:

try:
    linux_interaction()
    with open('file.log') as file:
        read_data = file.read()
except FileNotFoundError as fnf_error:
    print(fnf_error)
except AssertionError as error:
    print(error)
    print('Linux linux_interaction() function was not executed')

If the file does not exist, running this code on a Windows machine will output the following:

Function can only run on Linux systems.
Linux linux_interaction() function was not executed

Inside the try clause, you ran into an exception immediately and did not get to the part where you attempt to open file.log. Now look at what happens when you run the code on a Linux machine:

[Errno 2] No such file or directory: 'file.log'

Here are the key takeaways:

  • A try clause is executed up until the point where the first exception is encountered.
  • Inside the except clause, or the exception handler, you determine how the program responds to the exception.
  • You can anticipate multiple exceptions and differentiate how the program should respond to them.
  • Avoid using bare except clauses.

The else Clause

In Python, using the else statement, you can instruct a program to execute a certain block of code only in the absence of exceptions.

Diagram of try, except, and else statements in Python

Look at the following example:

try:
    linux_interaction()
except AssertionError as error:
    print(error)
else:
    print('Executing the else clause.')

If you were to run this code on a Linux system, the output would be the following:

Doing something.
Executing the else clause.

Because the program did not run into any exceptions, the else clause was executed.

You can also try to run code inside the else clause and catch possible exceptions there as well:

try:
    linux_interaction()
except AssertionError as error:
    print(error)
else:
    try:
        with open('file.log') as file:
            read_data = file.read()
    except FileNotFoundError as fnf_error:
        print(fnf_error)

If you were to execute this code on a Linux machine, you would get the following result:

Doing something.
[Errno 2] No such file or directory: 'file.log'

From the output, you can see that the linux_interaction() function ran. Because no exceptions were encountered, an attempt to open file.log was made. That file did not exist, and instead of opening the file, you caught the FileNotFoundError exception.

Cleaning Up After Using finally

Imagine that you always had to implement some sort of action to clean up after executing your code. Python enables you to do so using the finally clause.

Diagram explaining try except else finally statements

Have a look at the following example:

try:
    linux_interaction()
except AssertionError as error:
    print(error)
else:
    try:
        with open('file.log') as file:
            read_data = file.read()
    except FileNotFoundError as fnf_error:
        print(fnf_error)
finally:
    print('Cleaning up, irrespective of any exceptions.')

In the previous code, everything in the finally clause will be executed. It does not matter if you encounter an exception somewhere in the try or else clauses. Running the previous code on a Windows machine would output the following:

Function can only run on Linux systems.
Cleaning up, irrespective of any exceptions.

Summing Up

After seeing the difference between syntax errors and exceptions, you learned about various ways to raise, catch, and handle exceptions in Python. In this article, you saw the following options:

  • raise allows you to throw an exception at any time.
  • assert enables you to verify if a certain condition is met and throw an exception if it isn’t.
  • In the try clause, all statements are executed until an exception is encountered.
  • except is used to catch and handle the exception(s) that are encountered in the try clause.
  • else lets you code sections that should run only when no exceptions are encountered in the try clause.
  • finally enables you to execute sections of code that should always run, with or without any previously encountered exceptions.

Hopefully, this article helped you understand the basic tools that Python has to offer when dealing with exceptions.

Watch Now This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding: Raising and Handling Python Exceptions

In this article, let us learn about printing error messages from Exceptions with the help of 5 specifically chosen examples.

I have divided this article into 2 major sections

  1. Printing custom error messages and
  2. Printing a specific part of the default error message. By “default error message“, I mean the error message that you typically get in the command line if you did not catch a given exception)

Depending on which of the 2 options above you are looking for, you can jump to the respective section of the article using the table of content below.

So, let’s begin!

Printing Custom Error messages

There are 3 ways to print custom error messages in Python. Let us start with the simplest of the 3, which is using a print() statement.

Option#1: Using a simple print() statement

The first and easiest option is to print error messages using a simple print() statement as shown in the example below.

try:
    #Some Problematic code that can produce Exceptions
    x = 5/0
except Exception as e:
  print('A problem has occurred from the Problematic code: ', e)

Running this code will give the output below.

A problem has occurred from the Problematic code: division by zero

Here the line “x = 5/0″ in Example 1 above raised a “ZeroDivisionError” which was caught by our except clause and the print() statement printed the default error message which is “division by zero” to the standard output.

One thing to note here is the line “except Exception as e“. This line of code’s function is to catch all possible exceptions, whichever occurs first as an “Exception” object. This object is stored in the variable “e” (line 4), which returns the string ‘division by zero‘ when used with the print() statement (line 5).

To summarize if you wish to print out the default error message along with a custom message use Option#1.

This is the simplest way to print error messages in python. But this option of putting your custom messages into print statements might not work in cases where you might be handling a list of exceptions using a single except clause. If you are not exactly sure how to catch a list of exceptions using a single except clause, I suggest reading my other article in the link below.

Python: 3 Ways to Catch Multiple Exceptions in a single “except” clause

There I have explained the 3 ways through which you can catch a list of exceptions along with tips on when is the right situation to catch each of these exceptions.

Now that we have learned how to print the default string which comes with an exception object, let us next learn how to customize the message that e carried (the string ‘division by zero‘) and replace that with our own custom error message.

Option#2: Using Custom Exception classes to get customized error messages

In Python, you can define your own custom exception classes by inheriting from another Exception class as shown in the code below.

class MyOwnException(Exception):
    def __str__(self):
        return 'My Own Exception has occurred'

    def __repr__(self):
        return str(type(self))
try:
    raise MyOwnException
except MyOwnException as e:
    print(e)
    print(repr(e))

How to choose the exception class to inherit from?

In the above example, I have inherited from the Exception class in python, but the recommended practice is to choose a class that closely resembles your use-case.

For example, say you are trying to work with a string type object and you are given a list type object instead, here you should inherit your custom exception from TypeError since this Exception type closely resembles your use case which is “the variable is not of expected type”.

If you are looking for getting an appropriate Exception class to inherit from, I recommend having a look at all the built-in exceptions from the official python page here. For the sake of keeping this example simple, I have chosen the higher-level exception type named “Exception” class to inherit from.

In the code below, we are collecting values from the user and to tell the user that there is an error in the value entered we are using the ValueError class.

class EnteredGarbageError(ValueError):
    def __str__(self):
        return 'You did not select an option provided!'    

try:
    options = ['A', 'B', 'C']
    x = input('Type A or B or C: ')
    if x not in options:
        raise EnteredGarbageError
    else:
        print ('You have chosen: ', x)

except EnteredGarbageError as err:
    print(err)

Now that we understand how to choose a class to inherit from, let us next have a look at how to customize the default error messages that these classes return.

How to customize the error message in our custom exception class?

To help us achieve our purpose here which is to print some custom error messages, all objects in python come with 2 methods named __str__ and __repr__. This is pronounced “dunder-str” and “dunder-repr” where “dunder” is short for “double underscore”.

Dunder-str method:

The method __str__ returns a string and this is what the built-in print() function calls whenever we pass it an object to print.

print(object1)

In the line above, python will call the __str__ method of the object and prints out the string returned by that method.

Let us have a look at what python’s official documentation over at python.org has to say about the str method.

https://docs.python.org/3/reference/datamodel.html#object.str

In simpler words, the str method returns a human-readable string for logging purposes, and when this information is passed to the built-in function print(), the string it returns gets printed.

So since our implementation of str returns the string “My Own Exception has occurred” this string got printed on the first line of the exception message.

Dunder-repr method:

__repr__ is another method available in all objects in python.

Where it differs from the dunder-str method is the fact that while the __str__ is used for getting a “friendly message”, the __repr__ method is used for getting, a more of a, “formal message”. You can think of str as a text you got from your friends and repr as a notice you got from a legal representative!

The below screenshot from python’s official documentation explains the use of __repr__ method.

https://docs.python.org/3/reference/datamodel.html#object.repr

Again, in simpler words, repr is typically used to print some “formal” or “official” information about an object in Python

In our Example 2 above, the repr method returned the class name using the built-in type() function.

Next, let us see another variation where we can print different error messages using a single Exception class without making a custom class.

Option#3: Custom Error messages from the raise statement

try:
  raise Exception('I wish to print this message')
except Exception as error:
  print(error)

Lucky for us, python has made this process incredibly simple! Just pass in the message as an argument to the type of exception you wish to raise and this will print that custom message instead!

In the above code, we are throwing an exception of type “Exception” by calling its constructor and giving the custom message as an argument, which then overrides the default __str__ method to return the string passed in.

If you wish to learn more about raise statement, I suggest reading my other article in the link below
Python: Manually throw/raise an Exception using the “raise” statement

where I have explained 3 ways you can use the raise statement in python and when to use each.

But when to use option 2 and when to use option 3?

On the surface, Option#3 of passing in the custom message may look like it made option#2 of using custom classes useless. But the main reason to use Option#2 is the fact that Option#2 can be used to override more than just the __str__ method.

Let’s next move on to section 2 of this article and look at how to choose a specific part of the default error message (the error printed on the console when you don’t catch an exception) and use that to make our own error messages

Choosing Parts of Default Error Messages to print

To understand what I mean by “Default Error Message” let us see an example

raise ValueError("This is an ValueError")

This line when run, will print the following error message

Traceback (most recent call last):

  File "<ipython-input-24-57127e33a735>", line 1, in <module>
    raise ValueError("This is an ValueError")

ValueError: This is an ValueError

This error message contains 3 Parts

  • Exception Type (ValueError)
  • Error message (This is an ValueError)
  • and the stack trace (the 1st few lines showing us where exactly in the program the exception has occurred)

The information needed

  • to extract and use each of the individual pieces of information listed above and
  • when to use what piece of information

is already covered with the help of several examples in my previous article in the link below

Python Exceptions: Getting and Handling Error Messages as strings.

And with that I will end this article!

If you are looking for another interesting read, try the article in the link below.

Exceptions in Python: Everything You Need To Know!

The above article covers all the basics of Exception handling in Python like

  • when and how to ignore exceptions
  • when and how to retry the problematic code that produced the exception and
  • when and how to log the errors

I hope you enjoyed reading this article and got some value from it.

Feel free to share it with your friends and colleagues!

Improve Article

Save Article

  • Read
  • Discuss
  • Improve Article

    Save Article

    Errors are the problems in a program due to which the program will stop the execution. On the other hand, exceptions are raised when some internal events occur which changes the normal flow of the program. 
    Two types of Error occurs in python. 
     

    1. Syntax errors
    2. Logical errors (Exceptions) 
       

    Syntax errors

    When the proper syntax of the language is not followed then a syntax error is thrown.
    Example 
     

    Python3

    amount = 10000

    if(amount>2999)

        print("You are eligible to purchase Dsa Self Paced")

    Output:
     

    It returns a syntax error message because after the if statement a colon: is missing. We can fix this by writing the correct syntax.
     

    logical errors(Exception)

    When in the runtime an error that occurs after passing the syntax test is called exception or logical type. For example, when we divide any number by zero then the ZeroDivisionError exception is raised, or when we import a module that does not exist then ImportError is raised.
    Example 1: 
     

    Python3

    marks = 10000

    a = marks / 0

    print(a)

    Output:
     

    In the above example the ZeroDivisionError as we are trying to divide a number by 0.
    Example 2: When indentation is not correct. 
     

    Python3

    Output:
     

    Some of the common built-in exceptions are other than above mention exceptions are:
     

    Exception Description
    IndexError When the wrong index of a list is retrieved.
    AssertionError It occurs when the assert statement fails
    AttributeError It occurs when an attribute assignment is failed.
    ImportError It occurs when an imported module is not found.
    KeyError It occurs when the key of the dictionary is not found.
    NameError It occurs when the variable is not defined.
    MemoryError It occurs when a program runs out of memory.
    TypeError It occurs when a function and operation are applied in an incorrect type.

    Note: For more information, refer to Built-in Exceptions in Python
     

    Error Handling

    When an error and an exception are raised then we handle that with the help of the Handling method.
     

    • Handling Exceptions with Try/Except/Finally 
      We can handle errors by the Try/Except/Finally method. we write unsafe code in the try, fall back code in except and final code in finally block.
      Example 
       

    Python3

    try:

         print("code start")

         print(1 / 0)

    except:

         print("an error occurs")

    finally:

         print("GeeksForGeeks")

    • Output: 
       
    code start
    an error occurs
    GeeksForGeeks
    •  
    • Raising exceptions for a predefined condition 
      When we want to code for the limitation of certain conditions then we can raise an exception. 
      Example 
       

    Python3

    try:

        amount = 1999

        if amount < 2999:

            raise ValueError("please add money in your account")

        else:

            print("You are eligible to purchase DSA Self Paced course")

    except ValueError as e:

            print(e)

    • Output: 
       
    please add money in your account

    Overview

    Teaching: 30 min

    Exercises: 0 min

    Questions

    • How does Python report errors?

    • How can I handle errors in Python programs?

    Objectives

    • To be able to read a traceback, and determine where the error took place and what type it is.

    • To be able to describe the types of situations in which syntax errors, indentation errors, name errors, index errors, and missing file errors occur.

    Every programmer encounters errors,
    both those who are just beginning,
    and those who have been programming for years.
    Encountering errors and exceptions can be very frustrating at times,
    and can make coding feel like a hopeless endeavour.
    However,
    understanding what the different types of errors are
    and when you are likely to encounter them can help a lot.
    Once you know why you get certain types of errors,
    they become much easier to fix.

    Errors in Python have a very specific form,
    called a traceback.
    Let’s examine one:

    # This code has an intentional error. You can type it directly or
    # use it for reference to understand the error message below.
    def favorite_ice_cream():
        ice_creams = [
            'chocolate',
            'vanilla',
            'strawberry'
        ]
        print(ice_creams[3])
    
    favorite_ice_cream()
    
    ---------------------------------------------------------------------------
    IndexError                                Traceback (most recent call last)
    <ipython-input-1-70bd89baa4df> in <module>()
          9     print(ice_creams[3])
          10
    ----> 11 favorite_ice_cream()
    
    <ipython-input-1-70bd89baa4df> in favorite_ice_cream()
          7         'strawberry'
          8     ]
    ----> 9     print(ice_creams[3])
          10
          11 favorite_ice_cream()
    
    IndexError: list index out of range
    

    This particular traceback has two levels.
    You can determine the number of levels by looking for the number of arrows on the left hand side.
    In this case:

    1. The first shows code from the cell above,
      with an arrow pointing to Line 11 (which is favorite_ice_cream()).

    2. The second shows some code in the function favorite_ice_cream,
      with an arrow pointing to Line 9 (which is print(ice_creams[3])).

    The last level is the actual place where the error occurred.
    The other level(s) show what function the program executed to get to the next level down.
    So, in this case, the program first performed a
    function call to the function favorite_ice_cream.
    Inside this function,
    the program encountered an error on Line 6, when it tried to run the code print(ice_creams[3]).

    Long Tracebacks

    Sometimes, you might see a traceback that is very long
    – sometimes they might even be 20 levels deep!
    This can make it seem like something horrible happened,
    but the length of the error message does not reflect severity, rather,
    it indicates that your program called many functions before it encountered the error.
    Most of the time, the actual place where the error occurred is at the bottom-most level,
    so you can skip down the traceback to the bottom.

    So what error did the program actually encounter?
    In the last line of the traceback,
    Python helpfully tells us the category or type of error (in this case, it is an IndexError)
    and a more detailed error message (in this case, it says “list index out of range”).

    If you encounter an error and don’t know what it means,
    it is still important to read the traceback closely.
    That way,
    if you fix the error,
    but encounter a new one,
    you can tell that the error changed.
    Additionally,
    sometimes knowing where the error occurred is enough to fix it,
    even if you don’t entirely understand the message.

    If you do encounter an error you don’t recognize,
    try looking at the
    official documentation on errors.
    However,
    note that you may not always be able to find the error there,
    as it is possible to create custom errors.
    In that case,
    hopefully the custom error message is informative enough to help you figure out what went wrong.

    Syntax Errors

    When you forget a colon at the end of a line,
    accidentally add one space too many when indenting under an if statement,
    or forget a parenthesis,
    you will encounter a syntax error.
    This means that Python couldn’t figure out how to read your program.
    This is similar to forgetting punctuation in English:
    for example,
    this text is difficult to read there is no punctuation there is also no capitalization
    why is this hard because you have to figure out where each sentence ends
    you also have to figure out where each sentence begins
    to some extent it might be ambiguous if there should be a sentence break or not

    People can typically figure out what is meant by text with no punctuation,
    but people are much smarter than computers.
    If Python doesn’t know how to read the program,
    it will give up and inform you with an error.
    For example:

    def some_function()
        msg = 'hello, world!'
        print(msg)
         return msg
    
      File "<ipython-input-3-6bb841ea1423>", line 1
        def some_function()
                           ^
    SyntaxError: invalid syntax
    

    Here, Python tells us that there is a SyntaxError on line 1,
    and even puts a little arrow in the place where there is an issue.
    In this case the problem is that the function definition is missing a colon at the end.

    Actually, the function above has two issues with syntax.
    If we fix the problem with the colon,
    we see that there is also an IndentationError,
    which means that the lines in the function definition do not all have the same indentation:

    def some_function():
        msg = 'hello, world!'
        print(msg)
         return msg
    
      File "<ipython-input-4-ae290e7659cb>", line 4
        return msg
        ^
    IndentationError: unexpected indent
    

    Both SyntaxError and IndentationError indicate a problem with the syntax of your program,
    but an IndentationError is more specific:
    it always means that there is a problem with how your code is indented.

    Tabs and Spaces

    Some indentation errors are harder to spot than others.
    In particular, mixing spaces and tabs can be difficult to spot
    because they are both whitespace.
    In the example below, the first two lines in the body of the function
    some_function are indented with tabs, while the third line — with spaces.
    If you’re working in a Jupyter notebook, be sure to copy and paste this example
    rather than trying to type it in manually because Jupyter automatically replaces
    tabs with spaces.

    def some_function():
    	msg = 'hello, world!'
    	print(msg)
            return msg
    

    Visually it is impossible to spot the error.
    Fortunately, Python does not allow you to mix tabs and spaces.

      File "<ipython-input-5-653b36fbcd41>", line 4
        return msg
                  ^
    TabError: inconsistent use of tabs and spaces in indentation
    

    Variable Name Errors

    Another very common type of error is called a NameError,
    and occurs when you try to use a variable that does not exist.
    For example:

    ---------------------------------------------------------------------------
    NameError                                 Traceback (most recent call last)
    <ipython-input-7-9d7b17ad5387> in <module>()
    ----> 1 print(a)
    
    NameError: name 'a' is not defined
    

    Variable name errors come with some of the most informative error messages,
    which are usually of the form “name ‘the_variable_name’ is not defined”.

    Why does this error message occur?
    That’s a harder question to answer,
    because it depends on what your code is supposed to do.
    However,
    there are a few very common reasons why you might have an undefined variable.
    The first is that you meant to use a
    string, but forgot to put quotes around it:

    ---------------------------------------------------------------------------
    NameError                                 Traceback (most recent call last)
    <ipython-input-8-9553ee03b645> in <module>()
    ----> 1 print(hello)
    
    NameError: name 'hello' is not defined
    

    The second reason is that you might be trying to use a variable that does not yet exist.
    In the following example,
    count should have been defined (e.g., with count = 0) before the for loop:

    for number in range(10):
        count = count + number
    print('The count is:', count)
    
    ---------------------------------------------------------------------------
    NameError                                 Traceback (most recent call last)
    <ipython-input-9-dd6a12d7ca5c> in <module>()
          1 for number in range(10):
    ----> 2     count = count + number
          3 print('The count is:', count)
    
    NameError: name 'count' is not defined
    

    Finally, the third possibility is that you made a typo when you were writing your code.
    Let’s say we fixed the error above by adding the line Count = 0 before the for loop.
    Frustratingly, this actually does not fix the error.
    Remember that variables are case-sensitive,
    so the variable count is different from Count. We still get the same error,
    because we still have not defined count:

    Count = 0
    for number in range(10):
        count = count + number
    print('The count is:', count)
    
    ---------------------------------------------------------------------------
    NameError                                 Traceback (most recent call last)
    <ipython-input-10-d77d40059aea> in <module>()
          1 Count = 0
          2 for number in range(10):
    ----> 3     count = count + number
          4 print('The count is:', count)
    
    NameError: name 'count' is not defined
    

    Index Errors

    Next up are errors having to do with containers (like lists and strings) and the items within them.
    If you try to access an item in a list or a string that does not exist,
    then you will get an error.
    This makes sense:
    if you asked someone what day they would like to get coffee,
    and they answered “caturday”,
    you might be a bit annoyed.
    Python gets similarly annoyed if you try to ask it for an item that doesn’t exist:

    letters = ['a', 'b', 'c']
    print('Letter #1 is', letters[0])
    print('Letter #2 is', letters[1])
    print('Letter #3 is', letters[2])
    print('Letter #4 is', letters[3])
    
    Letter #1 is a
    Letter #2 is b
    Letter #3 is c
    
    ---------------------------------------------------------------------------
    IndexError                                Traceback (most recent call last)
    <ipython-input-11-d817f55b7d6c> in <module>()
          3 print('Letter #2 is', letters[1])
          4 print('Letter #3 is', letters[2])
    ----> 5 print('Letter #4 is', letters[3])
    
    IndexError: list index out of range
    

    Here,
    Python is telling us that there is an IndexError in our code,
    meaning we tried to access a list index that did not exist.

    File Errors

    The last type of error we’ll cover today
    are those associated with reading and writing files: FileNotFoundError.
    If you try to read a file that does not exist,
    you will receive a FileNotFoundError telling you so.
    If you attempt to write to a file that was opened read-only, Python 3
    returns an UnsupportedOperationError.
    More generally, problems with input and output manifest as
    IOErrors or OSErrors, depending on the version of Python you use.

    file_handle = open('myfile.txt', 'r')
    
    ---------------------------------------------------------------------------
    FileNotFoundError                         Traceback (most recent call last)
    <ipython-input-14-f6e1ac4aee96> in <module>()
    ----> 1 file_handle = open('myfile.txt', 'r')
    
    FileNotFoundError: [Errno 2] No such file or directory: 'myfile.txt'
    

    One reason for receiving this error is that you specified an incorrect path to the file.
    For example,
    if I am currently in a folder called myproject,
    and I have a file in myproject/writing/myfile.txt,
    but I try to open myfile.txt,
    this will fail.
    The correct path would be writing/myfile.txt.
    It is also possible that the file name or its path contains a typo.

    A related issue can occur if you use the “read” flag instead of the “write” flag.
    Python will not give you an error if you try to open a file for writing
    when the file does not exist.
    However,
    if you meant to open a file for reading,
    but accidentally opened it for writing,
    and then try to read from it,
    you will get an UnsupportedOperation error
    telling you that the file was not opened for reading:

    file_handle = open('myfile.txt', 'w')
    file_handle.read()
    
    ---------------------------------------------------------------------------
    UnsupportedOperation                      Traceback (most recent call last)
    <ipython-input-15-b846479bc61f> in <module>()
          1 file_handle = open('myfile.txt', 'w')
    ----> 2 file_handle.read()
    
    UnsupportedOperation: not readable
    

    These are the most common errors with files,
    though many others exist.
    If you get an error that you’ve never seen before,
    searching the Internet for that error type
    often reveals common reasons why you might get that error.

    Reading Error Messages

    Read the Python code and the resulting traceback below, and answer the following questions:

    1. How many levels does the traceback have?
    2. What is the function name where the error occurred?
    3. On which line number in this function did the error occur?
    4. What is the type of error?
    5. What is the error message?
    # This code has an intentional error. Do not type it directly;
    # use it for reference to understand the error message below.
    def print_message(day):
        messages = {
            'monday': 'Hello, world!',
            'tuesday': 'Today is Tuesday!',
            'wednesday': 'It is the middle of the week.',
            'thursday': 'Today is Donnerstag in German!',
            'friday': 'Last day of the week!',
            'saturday': 'Hooray for the weekend!',
            'sunday': 'Aw, the weekend is almost over.'
        }
        print(messages[day])
    
    def print_friday_message():
        print_message('Friday')
    
    print_friday_message()
    
    ---------------------------------------------------------------------------
    KeyError                                  Traceback (most recent call last)
    <ipython-input-1-4be1945adbe2> in <module>()
         14     print_message('Friday')
         15
    ---> 16 print_friday_message()
    
    <ipython-input-1-4be1945adbe2> in print_friday_message()
         12
         13 def print_friday_message():
    ---> 14     print_message('Friday')
         15
         16 print_friday_message()
    
    <ipython-input-1-4be1945adbe2> in print_message(day)
          9         'sunday': 'Aw, the weekend is almost over.'
         10     }
    ---> 11     print(messages[day])
         12
         13 def print_friday_message():
    
    KeyError: 'Friday'
    

    Solution

    1. 3 levels
    2. print_message
    3. 11
    4. KeyError
    5. There isn’t really a message; you’re supposed
      to infer that Friday is not a key in messages.

    Identifying Syntax Errors

    1. Read the code below, and (without running it) try to identify what the errors are.
    2. Run the code, and read the error message. Is it a SyntaxError or an IndentationError?
    3. Fix the error.
    4. Repeat steps 2 and 3, until you have fixed all the errors.
    def another_function
      print('Syntax errors are annoying.')
       print('But at least Python tells us about them!')
      print('So they are usually not too hard to fix.')
    

    Solution

    SyntaxError for missing (): at end of first line,
    IndentationError for mismatch between second and third lines.
    A fixed version is:

    def another_function():
        print('Syntax errors are annoying.')
        print('But at least Python tells us about them!')
        print('So they are usually not too hard to fix.')
    

    Identifying Variable Name Errors

    1. Read the code below, and (without running it) try to identify what the errors are.
    2. Run the code, and read the error message.
      What type of NameError do you think this is?
      In other words, is it a string with no quotes,
      a misspelled variable,
      or a variable that should have been defined but was not?
    3. Fix the error.
    4. Repeat steps 2 and 3, until you have fixed all the errors.
    for number in range(10):
        # use a if the number is a multiple of 3, otherwise use b
        if (Number % 3) == 0:
            message = message + a
        else:
            message = message + 'b'
    print(message)
    

    Solution

    3 NameErrors for number being misspelled, for message not defined,
    and for a not being in quotes.

    Fixed version:

    message = ''
    for number in range(10):
        # use a if the number is a multiple of 3, otherwise use b
        if (number % 3) == 0:
            message = message + 'a'
        else:
            message = message + 'b'
    print(message)
    

    Identifying Index Errors

    1. Read the code below, and (without running it) try to identify what the errors are.
    2. Run the code, and read the error message. What type of error is it?
    3. Fix the error.
    seasons = ['Spring', 'Summer', 'Fall', 'Winter']
    print('My favorite season is ', seasons[4])
    

    Solution

    IndexError; the last entry is seasons[3], so seasons[4] doesn’t make sense.
    A fixed version is:

    seasons = ['Spring', 'Summer', 'Fall', 'Winter']
    print('My favorite season is ', seasons[-1])
    

    Key Points

    • Tracebacks can look intimidating, but they give us a lot of useful information about what went wrong in our program, including where the error occurred and what type of error it was.

    • An error having to do with the ‘grammar’ or syntax of the program is called a SyntaxError. If the issue has to do with how the code is indented, then it will be called an IndentationError.

    • A NameError will occur when trying to use a variable that does not exist. Possible causes are that a variable definition is missing, a variable reference differs from its definition in spelling or capitalization, or the code contains a string that is missing quotes around it.

    • Containers like lists and strings will generate errors if you try to access items in them that do not exist. This type of error is called an IndexError.

    • Trying to read a file that does not exist will give you an FileNotFoundError. Trying to read a file that is open for writing, or writing to a file that is open for reading, will give you an IOError.

    How to Handle Exceptions in Python: A Detailed Visual Introduction

    Welcome! In this article, you will learn how to handle exceptions in Python.

    In particular, we will cover:

    • Exceptions
    • The purpose of exception handling
    • The try clause
    • The except clause
    • The else clause
    • The finally clause
    • How to raise exceptions

    Are you ready? Let’s begin! 😀

    1️⃣ Intro to Exceptions

    We will start with exceptions:

    • What are they?
    • Why are they relevant?
    • Why should you handle them?

    According to the Python documentation:

    Errors detected during execution are called exceptions and are not unconditionally fatal.

    Exceptions are raised when the program encounters an error during its execution. They disrupt the normal flow of the program and usually end it abruptly. To avoid this, you can catch them and handle them appropriately.

    You’ve probably seen them during your programming projects.

    If you’ve ever tried to divide by zero in Python, you must have seen this error message:

    >>> a = 5/0
    Traceback (most recent call last):
      File "<pyshell#1>", line 1, in <module>
        a = 5/0
    ZeroDivisionError: division by zero

    If you tried to index a string with an invalid index, you definitely got this error message:

    >>> a = "Hello, World"
    >>> a[456]
    Traceback (most recent call last):
      File "<pyshell#3>", line 1, in <module>
        a[456]
    IndexError: string index out of range

    These are examples of exceptions.

    🔹 Common Exceptions

    There are many different types of exceptions, and they are all raised in particular situations. Some of the exceptions that you will most likely see as you work on your projects are:

    • IndexError — raised when you try to index a list, tuple, or string beyond the permitted boundaries. For example:
    >>> num = [1, 2, 6, 5]
    >>> num[56546546]
    Traceback (most recent call last):
      File "<pyshell#7>", line 1, in <module>
        num[56546546]
    IndexError: list index out of range
    • KeyError — raised when you try to access the value of a key that doesn’t exist in a dictionary. For example:
    >>> students = {"Nora": 15, "Gino": 30}
    >>> students["Lisa"]
    Traceback (most recent call last):
      File "<pyshell#9>", line 1, in <module>
        students["Lisa"]
    KeyError: 'Lisa'
    • NameError — raised when a name that you are referencing in the code doesn’t exist. For example:
    >>> a = b
    Traceback (most recent call last):
      File "<pyshell#10>", line 1, in <module>
        a = b
    NameError: name 'b' is not defined
    • TypeError — raised when an operation or function is applied to an object of an inappropriate type. For example:
    >>> (5, 6, 7) * (1, 2, 3)
    Traceback (most recent call last):
      File "<pyshell#12>", line 1, in <module>
        (5, 6, 7) * (1, 2, 3)
    TypeError: can't multiply sequence by non-int of type 'tuple'
    • ZeroDivisionError — raised when you try to divide by zero.
    >>> a = 5/0
    Traceback (most recent call last):
      File "<pyshell#13>", line 1, in <module>
        a = 5/0
    ZeroDivisionError: division by zero

    💡 Tips: To learn more about other types of built-in exceptions, please refer to this article in the Python Documentation.

    🔸 Anatomy of an Exception

    I’m sure that you must have noticed a general pattern in these error messages. Let’s break down their general structure piece by piece:

    image-8

    First, we find this line (see below). A traceback is basically a list detailing the function calls that were made before the exception was raised.

    The traceback helps you during the debugging process because you can analyze the sequence of function calls that resulted in the exception:

    Traceback (most recent call last):

    Then, we see this line (see below) with the path to the file and the line that raised the exception. In this case, the path was the Python shell <pyshell#0> since the example was executed directly in IDLE.

    File "<pyshell#0>", line 1, in <module>
       a - 5/0

    💡 Tip: If the line that raised the exception belongs to a function, <module>  is replaced by the name of the function.

    Finally, we see a descriptive message detailing the type of exception and providing additional information to help us debug the code:

    NameError: name 'a' is not defined

    2️⃣ Exception Handling: Purpose & Context

    You may ask: why would I want to handle exceptions? Why is this helpful for me? By handling exceptions, you can provide an alternative flow of execution to avoid crashing your program unexpectedly.

    🔹 Example: User Input

    Imagine what would happen if a user who is working with your program enters an invalid input. This would raise an exception because an invalid operation was performed during the process.

    If your program doesn’t handle this correctly, it will crash suddenly and the user will have a very disappointing experience with your product.

    But if you do handle the exception, you will be able to provide an alternative to improve the experience of the user.

    Perhaps you could display a descriptive message asking the user to enter a valid input, or you could provide a default value for the input. Depending on the context, you can choose what to do when this happens, and this is the magic of error handling. It can save the day when unexpected things happen. ⭐️

    🔸 What Happens Behind the Scenes?

    Basically, when we handle an exception, we are telling the program what to do if the exception is raised. In that case, the «alternative» flow of execution will come to the rescue. If no exceptions are raised, the code will run as expected.

    image-10

    3️⃣ Time to Code: The try … except Statement

    Now that you know what exceptions are and why you should we handle them, we will start diving into the built-in tools that the Python languages offers for this purpose.

    First, we have the most basic statement: try … except.

    Let’s illustrate this with a simple example. We have this small program that asks the user to enter the name of a student to display his/her age:

    students = {"Nora": 15, "Gino": 30}
    
    def print_student_age():
        name = input("Please enter the name of the student: ")
        print(students[name])
    
    print_student_age()

    Notice how we are not validating user input at the moment, so the user might enter invalid values (names that are not in the dictionary) and the consequences would be catastrophic because the program would crash if a KeyError is raised:

    # User Input
    Please enter the name of the student: "Daniel"
    
    # Error Message
    Traceback (most recent call last):
      File "<path>", line 15, in <module>
        print_student_age()
      File "<path>", line 13, in print_student_age
        print(students[name])
    KeyError: '"Daniel"'

    🔹 Syntax

    We can handle this nicely using try … except. This is the basic syntax:

    image-11

    In our example, we would add the try … except statement within the function. Let’s break this down piece by piece:

    students = {"Nora": 15, "Gino": 30}
    
    def print_student_age():
        while True:
            try:
                name = input("Please enter the name of the student: ")
                print(students[name])
                break
            except:
                print("This name is not registered")
        
    
    print_student_age()

    If we «zoom in», we see the try … except statement:

    try:
    	name = input("Please enter the name of the student: ")
    	print(students[name])
    	break
    except:
    	print("This name is not registered")
    • When the function is called, the try clause will run. If no exceptions are raised, the program will run as expected.
    • But if an exception is raised in the try clause, the flow of execution will immediately jump to the except clause to handle the exception.

    💡 Note: This code is contained within a while loop to continue asking for user input if the value is invalid. This is an example:

    Please enter the name of the student: "Lulu"
    This name is not registered
    Please enter the name of the student: 

    This is great, right? Now we can continue asking for user input if the value is invalid.

    At the moment, we are handling all possible exceptions with the same except clause. But what if we only want to handle a specific type of exception? Let’s see how we could do this.

    🔸 Catching Specific Exceptions

    Since not all types of exceptions are handled in the same way, we can specify which exceptions we would like to handle with this syntax:

    image-15

    This is an example. We are handling the ZeroDivisionError exception in case the user enters zero as the denominator:

    def divide_integers():
        while True:
            try:
                a = int(input("Please enter the numerator: "))
                b = int(input("Please enter the denominator: "))
                print(a / b)
            except ZeroDivisionError:
                print("Please enter a valid denominator.")
    
    
    divide_integers()

    This would be the result:

    # First iteration
    Please enter the numerator: 5
    Please enter the denominator: 0
    Please enter a valid denominator. 
    
    # Second iteration
    Please enter the numerator: 5
    Please enter the denominator: 2
    2.5

    We are handling this correctly. But… if another type of exception is raised, the program will not handle it gracefully.

    Here we have an example of a ValueError because one of the values is a float, not an int:

    Please enter the numerator: 5
    Please enter the denominator: 0.5
    Traceback (most recent call last):
      File "<path>", line 53, in <module>
        divide_integers()
      File "<path>", line 47, in divide_integers
        b = int(input("Please enter the denominator: "))
    ValueError: invalid literal for int() with base 10: '0.5'

    We can customize how we handle different types of exceptions.

    🔹 Multiple Except Clauses

    To do this, we need to add multiple except clauses to handle different types of exceptions differently.

    According to the Python Documentation:

    A try statement may have more than one except clause, to specify handlers for different exceptions. At most one handler will be executed.

    In this example, we have two except clauses. One of them handles ZeroDivisionError and the other one handles ValueError, the two types of exceptions that could be raised in this try block.

    def divide_integers():
        while True:
            try:
                a = int(input("Please enter the numerator: "))
                b = int(input("Please enter the denominator: "))
                print(a / b)
            except ZeroDivisionError:
                print("Please enter a valid denominator.")
            except ValueError:
                print("Both values have to be integers.")
    
    
    divide_integers() 

    💡 Tip: You have to determine which types of exceptions might be raised in the try block to handle them appropriately.

    🔸 Multiple Exceptions, One Except Clause

    You can also choose to handle different types of exceptions with the same except clause.

    According to the Python Documentation:

    An except clause may name multiple exceptions as a parenthesized tuple.

    This is an example where we catch two exceptions (ZeroDivisionError and ValueError) with the same except clause:

    def divide_integers():
        while True:
            try:
                a = int(input("Please enter the numerator: "))
                b = int(input("Please enter the denominator: "))
                print(a / b)
            except (ZeroDivisionError, ValueError):
                print("Please enter valid integers.")
    
    divide_integers()

    The output would be the same for the two types of exceptions because they are handled by the same except clause:

    Please enter the numerator: 5
    Please enter the denominator: 0
    Please enter valid integers.
    Please enter the numerator: 0.5
    Please enter valid integers.
    Please enter the numerator: 

    🔹 Handling Exceptions Raised by Functions Called in the try Clause

    An interesting aspect of exception handling is that if an exception is raised in a function that was previously called in the try clause of another function and the function itself does not handle it, the caller will handle it if there is an appropriate except clause.

    According to the Python Documentation:

    Exception handlers don’t just handle exceptions if they occur immediately in the try clause, but also if they occur inside functions that are called (even indirectly) in the try clause.

    Let’s see an example to illustrate this:

    def f(i):
        try:
            g(i)
        except IndexError:
            print("Please enter a valid index")
    
    def g(i):
        a = "Hello"
        return a[i]
    
    f(50)

    We have the f function and the g function. f calls g in the try clause. With the argument 50, g will raise an IndexError because the index 50 is not valid for the string a.

    But g itself doesn’t handle the exception. Notice how there is no try … except statement in the g function. Since it doesn’t handle the exception, it «sends» it to f to see if it can handle it, as you can see in the diagram below:

    image-16

    Since f does know how to handle an IndexError, the situation is handled gracefully and this is the output:

    Please enter a valid index

    💡 Note: If f had not handled the exception, the program would have ended abruptly with the default error message for an IndexError.

    🔸 Accessing Specific Details of Exceptions

    Exceptions are objects in Python, so you can assign the exception that was raised to a variable. This way, you can print the default description of the exception and access its arguments.

    According to the Python Documentation:

    The except clause may specify a variable after the exception name. The variable is bound to an exception instance with the arguments stored in instance.args.

    Here we have an example (see below) were we assign the instance of ZeroDivisionError to the variable e. Then, we can use this variable within the except clause to access the type of the exception, its message, and arguments.

    def divide_integers():
        while True:
            try:
                a = int(input("Please enter the numerator: "))
                b = int(input("Please enter the denominator: "))
                print(a / b)
            # Here we assign the exception to the variable e
            except ZeroDivisionError as e:
                print(type(e))
                print(e)
                print(e.args)
    
    divide_integers()

    The corresponding output would be:

    Please enter the numerator: 5
    Please enter the denominator: 0
    
    # Type
    <class 'ZeroDivisionError'>
    
    # Message
    division by zero
    
    # Args
    ('division by zero',)

    💡 Tip: If you are familiar with special methods, according to the Python Documentation: «for convenience, the exception instance defines __str__() so the arguments can be printed directly without having to reference .args

    4️⃣ Now Let’s Add: The «else» Clause

    The else clause is optional, but it’s a great tool because it lets us execute code that should only run if no exceptions were raised in the try clause.

    image-17

    According to the Python Documentation:

    The tryexcept statement has an optional else clause, which, when present, must follow all except clauses. It is useful for code that must be executed if the try clause does not raise an exception.

    Here is an example of the use of the else clause:

    def divide_integers():
        while True:
            try:
                a = int(input("Please enter the numerator: "))
                b = int(input("Please enter the denominator: "))
                result = a / b
            except (ZeroDivisionError, ValueError):
                print("Please enter valid integers. The denominator can't be zero")
            else:
                print(result)
    
    divide_integers()

    If no exception are raised, the result is printed:

    Please enter the numerator: 5
    Please enter the denominator: 5
    1.0

    But if an exception is raised, the result is not printed:

    Please enter the numerator: 5
    Please enter the denominator: 0
    Please enter valid integers. The denominator can't be zero

    💡 Tip: According to the Python Documentation:

    The use of the else clause is better than adding additional code to the try clause because it avoids accidentally catching an exception that wasn’t raised by the code being protected by the tryexcept statement.

    5️⃣ The «finally» Clause

    The finally clause is the last clause in this sequence. It is optional, but if you include it, it has to be the last clause in the sequence. The finally clause is always executed, even if an exception was raised in the try clause.  

    image-19

    According to the Python Documentation:

    If a finally clause is present, the finally clause will execute as the last task before the try statement completes. The finally clause runs whether or not the try statement produces an exception.

    The finally clause is usually used to perform «clean-up» actions that should always be completed. For example, if we are working with a file in the try clause, we will always need to close the file, even if an exception was raised when we were working with the data.

    Here is an example of the finally clause:

    def divide_integers():
        while True:
            try:
                a = int(input("Please enter the numerator: "))
                b = int(input("Please enter the denominator: "))
                result = a / b
            except (ZeroDivisionError, ValueError):
                print("Please enter valid integers. The denominator can't be zero")
            else:
                print(result)
            finally:
                print("Inside the finally clause")
    
    divide_integers()

    This is the output when no exceptions were raised:

    Please enter the numerator: 5
    Please enter the denominator: 5
    1.0
    Inside the finally clause

    This is the output when an exception was raised:

    Please enter the numerator: 5
    Please enter the denominator: 0
    Please enter valid integers. The denominator can't be zero
    Inside the finally clause

    Notice how the finally clause always runs.

    ❗️Important: remember that the else clause and the finally clause are optional, but if you decide to include both, the finally clause has to be the last clause in the sequence.

    6️⃣ Raising Exceptions

    Now that you know how to handle exceptions in Python, I would like to share with you this helpful tip: you can also choose when to raise exceptions in your code.

    This can be helpful for certain scenarios. Let’s see how you can do this:

    image-20

    This line will raise a ValueError with a custom message.

    Here we have an example (see below) of a function that prints the value of the items of a list or tuple, or the characters in a string. But you decided that you want the list, tuple, or string to be of length 5. You start the function with an if statement that checks if the length of the argument data is 5. If it isn’t, a ValueError exception is raised:

    def print_five_items(data):
        
        if len(data) != 5:
            raise ValueError("The argument must have five elements")
        
        for item in data:
            print(item)
    
    print_five_items([5, 2])

    The output would be:

    Traceback (most recent call last):
      File "<path>", line 122, in <module>
        print_five_items([5, 2])
      File "<path>", line 117, in print_five_items
        raise ValueError("The argument must have five elements")
    ValueError: The argument must have five elements

    Notice how the last line displays the descriptive message:

    ValueError: The argument must have five elements

    You can then choose how to handle the exception with a try … except statement. You could add an else clause and/or a finally clause. You can customize it to fit your needs.

    🔹 Helpful Resources

    • Exceptions
    • Handling Exceptions
    • Defining Clean-up Actions

    I hope you enjoyed reading my article and found it helpful. Now you have the necessary tools to handle exceptions in Python and you can use them to your advantage when you write Python code. ? Check out my online courses. You can follow me on Twitter.

    ⭐️ You may enjoy my other freeCodeCamp /news articles:

    • The @property Decorator in Python: Its Use Cases, Advantages, and Syntax
    • Data Structures 101: Graphs — A Visual Introduction for Beginners
    • Data Structures 101: Arrays — A Visual Introduction for Beginners


    Learn to code for free. freeCodeCamp’s open source curriculum has helped more than 40,000 people get jobs as developers. Get started

    In this article, you will learn error and exception handling in Python.

    By the end of the article, you’ll know:

    • How to handle exceptions using the try, except, and finally statements
    • How to create a custom exception
    • How to raise an exceptions
    • How to use built-in exception effectively to build robust Python programs
    Python Exceptions
    Python Exceptions

    Table of contents

    • What are Exceptions?
      • Why use Exception
    • What are Errors?
      • Syntax error
      • Logical errors (Exception)
    • Built-in Exceptions
    • The try and except Block to Handling Exceptions
      • Catching Specific Exceptions
      • Handle multiple exceptions with a single except clause
    • Using try with finally
    • Using try with else clause
    • Raising an Exceptions
    • Exception Chaining
    • Custom and User-defined Exceptions
      • Customizing Exception Classes
    • Exception Lifecycle
    • Warnings

    What are Exceptions?

    An exception is an event that occurs during the execution of programs that disrupt the normal flow of execution (e.g., KeyError Raised when a key is not found in a dictionary.) An exception is a Python object that represents an error..

    In Python, an exception is an object derives from the BaseException class that contains information about an error event that occurred within a method. Exception object contains:

    • Error type (exception name)
    • The state of the program when the error occurred
    • An error message describes the error event.

    Exception are useful to indicate different types of possible failure condition.

    For example, bellow are the few standard exceptions

    • FileNotFoundException
    • ImportError
    • RuntimeError
    • NameError
    • TypeError

    In Python, we can throw an exception in the try block and catch it in except block.

    Why use Exception

    • Standardized error handling: Using built-in exceptions or creating a custom exception with a more precise name and description, you can adequately define the error event, which helps you debug the error event.
    • Cleaner code: Exceptions separate the error-handling code from regular code, which helps us to maintain large code easily.
    • Robust application: With the help of exceptions, we can develop a solid application, which can handle error event efficiently
    • Exceptions propagation: By default, the exception propagates the call stack if you don’t catch it. For example, if any error event occurred in a nested function, you do not have to explicitly catch-and-forward it; automatically, it gets forwarded to the calling function where you can handle it.
    • Different error types: Either you can use built-in exception or create your custom exception and group them by their generalized parent class, or Differentiate errors by their actual class

    What are Errors?

    On the other hand, An error is an action that is incorrect or inaccurate. For example, syntax error. Due to which the program fails to execute.

    The errors can be broadly classified into two types:

    1. Syntax errors
    2. Logical errors

    Syntax error

    The syntax error occurs when we are not following the proper structure or syntax of the language. A syntax error is also known as a parsing error.

    When Python parses the program and finds an incorrect statement it is known as a syntax error. When the parser found a syntax error it exits with an error message without running anything.

    Common Python Syntax errors:

    • Incorrect indentation
    • Missing colon, comma, or brackets
    • Putting keywords in the wrong place.

    Example

    print("Welcome to PYnative")
        print("Learn Python with us..")

    Output

    print("Learn Python with us..")
        ^
    IndentationError: unexpected indent

    Logical errors (Exception)

    Even if a statement or expression is syntactically correct, the error that occurs at the runtime is known as a Logical error or Exception. In other words, Errors detected during execution are called exceptions.

    Common Python Logical errors:

    • Indenting a block to the wrong level
    • using the wrong variable name
    • making a mistake in a boolean expression

    Example

    a = 10
    b = 20
    print("Addition:", a + c)

    Output

    print("Addition:", a + c)
    NameError: name 'c' is not defined

    Built-in Exceptions

    The below table shows different built-in exceptions.

    Python automatically generates many exceptions and errors. Runtime exceptions, generally a result of programming errors, such as:

    • Reading a file that is not present
    • Trying to read data outside the available index of a list
    • Dividing an integer value by zero
    Exception Description
    AssertionError Raised when an assert statement fails.
    AttributeError Raised when attribute assignment or reference fails.
    EOFError Raised when the input() function hits the end-of-file condition.
    FloatingPointError Raised when a floating-point operation fails.
    GeneratorExit Raise when a generator’s close() method is called.
    ImportError Raised when the imported module is not found.
    IndexError Raised when the index of a sequence is out of range.
    KeyError Raised when a key is not found in a dictionary.
    KeyboardInterrupt Raised when the user hits the interrupt key (Ctrl+C or Delete)
    MemoryError Raised when an operation runs out of memory.
    NameError Raised when a variable is not found in the local or global scope.
    OSError Raised when system operation causes system related error.
    ReferenceError Raised when a weak reference proxy is used to access a garbage collected referent.
    Python Built-in Exceptions

    Example: The FilenotfoundError is raised when a file in not present on the disk

    fp = open("test.txt", "r")
    if fp:
        print("file is opened successfully")
    

    Output:

    FileNotFoundError: [Errno 2] No such file or directory: 'test.txt'

    The try and except Block to Handling Exceptions

    When an exception occurs, Python stops the program execution and generates an exception message. It is highly recommended to handle exceptions. The doubtful code that may raise an exception is called risky code.

    To handle exceptions we need to use try and except block. Define risky code that can raise an exception inside the try block and corresponding handling code inside the except block.

    Syntax

    try :
        # statements in try block
    except :
        # executed when exception occured in try block
    try-except block
    try-except block

    The try block is for risky code that can raise an exception and the except block to handle error raised in a try block. For example, if we divide any number by zero, try block will throw ZeroDivisionError, so we should handle that exception in the except block.

    When we do not use try…except block in the program, the program terminates abnormally, or it will be nongraceful termination of the program.

    Now let’s see the example when we do not use try…except block for handling exceptions.

    Example:

    a = 10
    b = 0
    c = a / b
    print("a/b = %d" % c)

    Output

    Traceback (most recent call last):
      File "E:/demos/exception.py", line 3, in <module>
        c = a / b
    ZeroDivisionError: division by zero

    We can see in the above code when we are divided by 0; Python throws an exception as ZeroDivisionError and the program terminated abnormally.

    We can handle the above exception using the try…except block. See the following code.

    Example

    try:
        a = 10
        b = 0
        c = a/b
        print("The answer of a divide by b:", c)
    except:
        print("Can't divide with zero. Provide different number")

    Output

    Can't divide with zero. Provide different number

    Catching Specific Exceptions

    We can also catch a specific exception. In the above example, we did not mention any specific exception in the except block. Catch all the exceptions and handle every exception is not good programming practice.

    It is good practice to specify an exact exception that the except clause should catch. For example, to catch an exception that occurs when the user enters a non-numerical value instead of a number, we can catch only the built-in ValueError exception that will handle such an event properly.

    We can specify which exception except block should catch or handle. A try block can be followed by multiple numbers of except blocks to handle the different exceptions. But only one exception will be executed when an exception occurs.

    Example

    In this example, we will ask the user for the denominator value. If the user enters a number, the program will evaluate and produce the result.

    If the user enters a non-numeric value then, the try block will throw a ValueError exception, and we can catch that using a first catch block ‘except ValueError’ by printing the message ‘Entered value is wrong’.

    And suppose the user enters the denominator as zero. In that case, the try block will throw a ZeroDivisionError, and we can catch that using a second catch block by printing the message ‘Can’t divide by zero’.

    try:
        a = int(input("Enter value of a:"))
        b = int(input("Enter value of b:"))
        c = a/b
        print("The answer of a divide by b:", c)
    except ValueError:
        print("Entered value is wrong")
    except ZeroDivisionError:
        print("Can't divide by zero")

    Output 1:

    Enter value of a:Ten
    Entered value is wrong

    Output 2:

    Enter value of a:10
    Enter value of b:0
    Can't divide by zero

    Output 3:

    Enter value of a:10
    Enter value of b:2
    The answer of a divide by b: 5.0

    Handle multiple exceptions with a single except clause

    We can also handle multiple exceptions with a single except clause. For that, we can use an tuple of values to specify multiple exceptions in an except clause.

    Example

    Let’s see how to specifiy two exceptions in the single except clause.

    try:
        a = int(input("Enter value of a:"))
        b = int(input("Enter value of b:"))
        c = a / b
        print("The answer of a divide by b:", c)
    except(ValueError, ZeroDivisionError):
        print("Please enter a valid value")

    Using try with finally

    Python provides the finally block, which is used with the try block statement. The finally block is used to write a block of code that must execute, whether the try block raises an error or not.

    Mainly, the finally block is used to release the external resource. This block provides a guarantee of execution.

    try-except-finally block
    try-except-finally block

    Clean-up actions using finally

    Sometimes we want to execute some action at any cost, even if an error occurred in a program. In Python, we can perform such actions using a finally statement with a try and except statement.

    The block of code written in the finally block will always execute even there is an exception in the try and except block.

    If an exception is not handled by except clause, then finally block executes first, then the exception is thrown. This process is known as clean-up action.

    Syntax

    try:    
        # block of code     
        # this may throw an exception    
    finally:    
        # block of code    
        # this will always be executed 
        # after the try and any except block   

    Example

    try:
        a = int(input("Enter value of a:"))
        b = int(input("Enter value of b:"))
        c = a / b
        print("The answer of a divide by b:", c)
    
    except ZeroDivisionError:
        print("Can't divide with zero")
    finally:
        print("Inside a finally block")

    Output 1:

    Enter value of a:20
    Enter value of b:5
    The answer of a divide by b: 4.0
    Inside a finally block

    Output 2:

    Enter value of a:20
    Enter value of b:0
    Can't divide with zero
    Inside a finally block

    In the above example, we can see we divide a number by 0 and get an error, and the program terminates normally. In this case, the finally block was also executed.

    Using try with else clause

    Sometimes we might want to run a specific block of code. In that case, we can use else block with the try-except block. The else block will be executed if and only if there are no exception is the try block. For these cases, we can use the optional else statement with the try statement.

    Why to use else block with try?

    Use else statemen with try block to check if try block executed without any exception or if you want to run a specific code only if an exception is not raised

    try-else block

    Syntax

    try:    
        # block of code     
    except Exception1:    
        # block of code     
    else:    
        # this code executes when exceptions not occured    
    • try: The try block for risky code that can throw an exception.
    • except: The except block to handle error raised in a try block.
    • else: The else block is executed if there is no exception.

    Example

    try:
        a = int(input("Enter value of a:"))
        b = int(input("Enter value of b:"))
        c = a / b
        print("a/b = %d" % c)
    
    except ZeroDivisionError:
        print("Can't divide by zero")
    else:
        print("We are in else block ")

    Output 1

    Enter value of a: 20
    Enter value of b:4
    a/b = 5
    We are in else block 

    Output 2

    Enter value of a: 20
    Enter value of b:0
    Can't divide by zero

    Raising an Exceptions

    In Python, the raise statement allows us to throw an exception. The single arguments in the raise statement show an exception to be raised. This can be either an exception object or an Exception class that is derived from the Exception class.

    The raise statement is useful in situations where we need to raise an exception to the caller program. We can raise exceptions in cases such as wrong data received or any validation failure.

    Follow the below steps to raise an exception:

    • Create an exception of the appropriate type. Use the existing built-in exceptions or create your won exception as per the requirement.
    • Pass the appropriate data while raising an exception.
    • Execute a raise statement, by providing the exception class.

    The syntax to use the raise statement is given below.

    raise Exception_class,<value>  

    Example

    In this example, we will throw an exception if interest rate is greater than 100.

    def simple_interest(amount, year, rate):
        try:
            if rate > 100:
                raise ValueError(rate)
            interest = (amount * year * rate) / 100
            print('The Simple Interest is', interest)
            return interest
        except ValueError:
            print('interest rate is out of range', rate)
    
    print('Case 1')
    simple_interest(800, 6, 8)
    
    print('Case 2')
    simple_interest(800, 6, 800)

    Output:

    Case 1
    The Simple Interest is 384.0
    
    Case 2
    interest rate is out of range 800

    Exception Chaining

    The exception chaining is available only in Python 3. The raise statements allow us as optional from statement, which enables chaining exceptions. So we can implement exception chaining in python3 by using raise…from clause to chain exception.

    When exception raise, exception chaining happens automatically. The exception can be raised inside except or finally block section. We also disabled exception chaining by using from None idiom.

    Example

    try:
        a = int(input("Enter value of a:"))
        b = int(input("Enter value of b:"))
        c = a/b
        print("The answer of a divide by b:", c)
    except ZeroDivisionError as e:
        raise ValueError("Division failed") from e
    
    # Output: Enter value of a:10
    # Enter value of b:0
    # ValueError: Division failed

    In the above example, we use exception chaining using raise...from clause and raise ValueError division failed.

    Custom and User-defined Exceptions

    Sometimes we have to define and raise exceptions explicitly to indicate that something goes wrong. Such a type of exception is called a user-defined exception or customized exception.

    The user can define custom exceptions by creating a new class. This new exception class has to derive either directly or indirectly from the built-in class Exception. In Python, most of the built-in exceptions also derived from the Exception class.

    class Error(Exception):
        """Base class for other exceptions"""
        pass
    
    class ValueTooSmallError(Error):
        """Raised when the input value is small"""
        pass
    
    class ValueTooLargeError(Error):
        """Raised when the input value is large"""
        pass
    
    while(True):
        try:
            num = int(input("Enter any value in 10 to 50 range: "))
            if num < 10:
                raise ValueTooSmallError
            elif num > 50:
                raise ValueTooLargeError
            break
        except ValueTooSmallError:
                print("Value is below range..try again")
    
        except ValueTooLargeError:
                print("value out of range...try again")
    
    print("Great! value in correct range.")

    Output

    Enter any value in 10 to 50 range: 5
    Value is below range..try again
    
    Enter any value in 10 to 50 range: 60
    value out of range...try again
    
    Enter any value in 10 to 50 range: 11
    Great! value in correct range.

    In the above example, we create two custom classes or user-defined classes with names, ValueTooSmallError and ValueTooLargeError.When the entered value is below the range, it will raise ValueTooSmallError and if the value is out of then, it will raise ValueTooLargeError.

    Customizing Exception Classes

    We can customize the classes by accepting arguments as per our requirements. Any custom exception class must be Extending from BaseException class or subclass of BaseException.

    In the above example, we create a custom class that is inherited from the base class Exception. This class takes one argument age. When entered age is negative, it will raise NegativeAgeError.

    class NegativeAgeError(Exception):
    
        def __init__(self, age, ):
            message = "Age should not be negative"
            self.age = age
            self.message = message
    
    age = int(input("Enter age: "))
    if age < 0:
        raise NegativeAgeError(age)
    # Output:
    # raise NegativeAgeError(age)
    # __main__.NegativeAgeError: -9
    

    Output:

    Enter age: -28
     Traceback (most recent call last):
       File "E:/demos/exception.py", line 11, in 
         raise NegativeAgeError(age)
     main.NegativeAgeError: -28

    Done

    Exception Lifecycle

    • When an exception is raised, The runtime system attempts to find a handler for the exception by backtracking the ordered list of methods calls. This is known as the call stack.
    • If a handler is found (i.e., if except block is located), there are two cases in the except block; either exception is handled or possibly re-thrown.
    • If the handler is not found (the runtime backtracks to the method chain’s last calling method), the exception stack trace is printed to the standard error console, and the application stops its execution.

    Example

    def sum_of_list(numbers):
        return sum(numbers)
    
    def average(sum, n):
        # ZeroDivisionError if list is empty
        return sum / n
    
    def final_data(data):
        for item in data:
            print("Average:", average(sum_of_list(item), len(item)))
    
    list1 = [10, 20, 30, 40, 50]
    list2 = [100, 200, 300, 400, 500]
    # empty list
    list3 = []
    lists = [list1, list2, list3]
    final_data(lists)

    Output

    Average: 30.0
    Traceback (most recent call last):
    File "E:/demos/exceptions.py", line 17, in
    final_data(lists)
    File "E:/demos/exceptions.py", line 11, in final_data
    print("Average:", average(sum_of_list(item), len(item)))
    Average: 300.0
    File "E:/demos/exceptions.py", line 6, in average
    return sum / n
    ZeroDivisionError: division by zero

    The above stack trace shows the methods that are being called from main() until the method created an exception condition. It also shows line numbers.

    Warnings

    Several built-in exceptions represent warning categories. This categorization is helpful to be able to filter out groups of warnings.

    The warning doesn’t stop the execution of a program it indicates the possible improvement

    Below is the list of warning exception

    Waring Class Meaning
    Warning Base class for warning categories
    UserWarning Base class for warnings generated by user code
    DeprecationWarning Warnings about deprecated features
    PendingDeprecationWarning Warnings about features that are obsolete and expected to be deprecated in the future, but are not deprecated at the moment.
    SyntaxWarning Warnings about dubious syntax
    RuntimeWarning Warnings about the dubious runtime behavior
    FutureWarning Warnings about probable mistakes in module imports
    ImportWarning Warnings about probable mistakes in module imports
    UnicodeWarning Warnings related to Unicode data
    BytesWarning Warnings related to bytes and bytearray.
    ResourceWarning Warnings related to resource usage
    Python Exception warnings

    Обработка ошибок увеличивает отказоустойчивость кода, защищая его от потенциальных сбоев, которые могут привести к преждевременному завершению работы.

    Синтаксис обработки исключений

    Прежде чем переходить к обсуждению того, почему обработка исключений так важна, и рассматривать встроенные в Python исключения, важно понять, что есть тонкая грань между понятиями ошибки и исключения.

    Ошибку нельзя обработать, а исключения Python обрабатываются при выполнении программы. Ошибка может быть синтаксической, но существует и много видов исключений, которые возникают при выполнении и не останавливают программу сразу же. Ошибка может указывать на критические проблемы, которые приложение и не должно перехватывать, а исключения — состояния, которые стоит попробовать перехватить. Ошибки — вид непроверяемых и невозвратимых ошибок, таких как OutOfMemoryError, которые не стоит пытаться обработать.

    Обработка исключений делает код более отказоустойчивым и помогает предотвращать потенциальные проблемы, которые могут привести к преждевременной остановке выполнения. Представьте код, который готов к развертыванию, но все равно прекращает работу из-за исключения. Клиент такой не примет, поэтому стоит заранее обработать конкретные исключения, чтобы избежать неразберихи.

    Ошибки могут быть разных видов:

    • Синтаксические
    • Недостаточно памяти
    • Ошибки рекурсии
    • Исключения

    Разберем их по очереди.

    Синтаксические ошибки (SyntaxError)

    Синтаксические ошибки часто называют ошибками разбора. Они возникают, когда интерпретатор обнаруживает синтаксическую проблему в коде.

    Рассмотрим на примере.

    a = 8
    b = 10
    c = a b
    
    File "", line 3
     c = a b
           ^
    SyntaxError: invalid syntax
    

    Стрелка вверху указывает на место, где интерпретатор получил ошибку при попытке исполнения. Знак перед стрелкой указывает на причину проблемы. Для устранения таких фундаментальных ошибок Python будет делать большую часть работы за программиста, выводя название файла и номер строки, где была обнаружена ошибка.

    Недостаточно памяти (OutofMemoryError)

    Ошибки памяти чаще всего связаны с оперативной памятью компьютера и относятся к структуре данных под названием “Куча” (heap). Если есть крупные объекты (или) ссылки на подобные, то с большой долей вероятности возникнет ошибка OutofMemory. Она может появиться по нескольким причинам:

    • Использование 32-битной архитектуры Python (максимальный объем выделенной памяти невысокий, между 2 и 4 ГБ);
    • Загрузка файла большого размера;
    • Запуск модели машинного обучения/глубокого обучения и много другое;

    Обработать ошибку памяти можно с помощью обработки исключений — резервного исключения. Оно используется, когда у интерпретатора заканчивается память и он должен немедленно остановить текущее исполнение. В редких случаях Python вызывает OutofMemoryError, позволяя скрипту каким-то образом перехватить самого себя, остановить ошибку памяти и восстановиться.

    Но поскольку Python использует архитектуру управления памятью из языка C (функция malloc()), не факт, что все процессы восстановятся — в некоторых случаях MemoryError приведет к остановке. Следовательно, обрабатывать такие ошибки не рекомендуется, и это не считается хорошей практикой.

    Ошибка рекурсии (RecursionError)

    Эта ошибка связана со стеком и происходит при вызове функций. Как и предполагает название, ошибка рекурсии возникает, когда внутри друг друга исполняется много методов (один из которых — с бесконечной рекурсией), но это ограничено размером стека.

    Все локальные переменные и методы размещаются в стеке. Для каждого вызова метода создается стековый кадр (фрейм), внутрь которого помещаются данные переменной или результат вызова метода. Когда исполнение метода завершается, его элемент удаляется.

    Чтобы воспроизвести эту ошибку, определим функцию recursion, которая будет рекурсивной — вызывать сама себя в бесконечном цикле. В результате появится ошибка StackOverflow или ошибка рекурсии, потому что стековый кадр будет заполняться данными метода из каждого вызова, но они не будут освобождаться.

    def recursion():
        return recursion()
    
    recursion()
    
    ---------------------------------------------------------------------------
    
    RecursionError                            Traceback (most recent call last)
    
     in 
    ----> 1 recursion()
    
    
     in recursion()
          1 def recursion():
    ----> 2     return recursion()
    
    
    ... last 1 frames repeated, from the frame below ...
    
    
     in recursion()
          1 def recursion():
    ----> 2     return recursion()
    
    
    RecursionError: maximum recursion depth exceeded
    

    Ошибка отступа (IndentationError)

    Эта ошибка похожа по духу на синтаксическую и является ее подвидом. Тем не менее она возникает только в случае проблем с отступами.

    Пример:

    for i in range(10):
        print('Привет Мир!')
    
      File "", line 2
        print('Привет Мир!')
            ^
    IndentationError: expected an indented block
    

    Исключения

    Даже если синтаксис в инструкции или само выражение верны, они все равно могут вызывать ошибки при исполнении. Исключения Python — это ошибки, обнаруживаемые при исполнении, но не являющиеся критическими. Скоро вы узнаете, как справляться с ними в программах Python. Объект исключения создается при вызове исключения Python. Если скрипт не обрабатывает исключение явно, программа будет остановлена принудительно.

    Программы обычно не обрабатывают исключения, что приводит к подобным сообщениям об ошибке:

    Ошибка типа (TypeError)

    a = 2
    b = 'PythonRu'
    a + b
    
    ---------------------------------------------------------------------------
    
    TypeError                                 Traceback (most recent call last)
    
     in 
          1 a = 2
          2 b = 'PythonRu'
    ----> 3 a + b
    
    
    TypeError: unsupported operand type(s) for +: 'int' and 'str'
    

    Ошибка деления на ноль (ZeroDivisionError)

    10 / 0
    
    ---------------------------------------------------------------------------
    
    ZeroDivisionError                         Traceback (most recent call last)
    
     in 
    ----> 1 10 / 0
    
    
    ZeroDivisionError: division by zero
    

    Есть разные типы исключений в Python и их тип выводится в сообщении: вверху примеры TypeError и ZeroDivisionError. Обе строки в сообщениях об ошибке представляют собой имена встроенных исключений Python.

    Оставшаяся часть строки с ошибкой предлагает подробности о причине ошибки на основе ее типа.

    Теперь рассмотрим встроенные исключения Python.

    Встроенные исключения

    BaseException
     +-- SystemExit
     +-- KeyboardInterrupt
     +-- GeneratorExit
     +-- Exception
          +-- StopIteration
          +-- StopAsyncIteration
          +-- ArithmeticError
          |    +-- FloatingPointError
          |    +-- OverflowError
          |    +-- ZeroDivisionError
          +-- AssertionError
          +-- AttributeError
          +-- BufferError
          +-- EOFError
          +-- ImportError
          |    +-- ModuleNotFoundError
          +-- LookupError
          |    +-- IndexError
          |    +-- KeyError
          +-- MemoryError
          +-- NameError
          |    +-- UnboundLocalError
          +-- OSError
          |    +-- BlockingIOError
          |    +-- ChildProcessError
          |    +-- ConnectionError
          |    |    +-- BrokenPipeError
          |    |    +-- ConnectionAbortedError
          |    |    +-- ConnectionRefusedError
          |    |    +-- ConnectionResetError
          |    +-- FileExistsError
          |    +-- FileNotFoundError
          |    +-- InterruptedError
          |    +-- IsADirectoryError
          |    +-- NotADirectoryError
          |    +-- PermissionError
          |    +-- ProcessLookupError
          |    +-- TimeoutError
          +-- ReferenceError
          +-- RuntimeError
          |    +-- NotImplementedError
          |    +-- RecursionError
          +-- SyntaxError
          |    +-- IndentationError
          |         +-- TabError
          +-- SystemError
          +-- TypeError
          +-- ValueError
          |    +-- UnicodeError
          |         +-- UnicodeDecodeError
          |         +-- UnicodeEncodeError
          |         +-- UnicodeTranslateError
          +-- Warning
               +-- DeprecationWarning
               +-- PendingDeprecationWarning
               +-- RuntimeWarning
               +-- SyntaxWarning
               +-- UserWarning
               +-- FutureWarning
               +-- ImportWarning
               +-- UnicodeWarning
               +-- BytesWarning
               +-- ResourceWarning
    

    Прежде чем переходить к разбору встроенных исключений быстро вспомним 4 основных компонента обработки исключения, как показано на этой схеме.

    • Try: он запускает блок кода, в котором ожидается ошибка.
    • Except: здесь определяется тип исключения, который ожидается в блоке try (встроенный или созданный).
    • Else: если исключений нет, тогда исполняется этот блок (его можно воспринимать как средство для запуска кода в том случае, если ожидается, что часть кода приведет к исключению).
    • Finally: вне зависимости от того, будет ли исключение или нет, этот блок кода исполняется всегда.

    В следующем разделе руководства больше узнаете об общих типах исключений и научитесь обрабатывать их с помощью инструмента обработки исключения.

    Ошибка прерывания с клавиатуры (KeyboardInterrupt)

    Исключение KeyboardInterrupt вызывается при попытке остановить программу с помощью сочетания Ctrl + C или Ctrl + Z в командной строке или ядре в Jupyter Notebook. Иногда это происходит неумышленно и подобная обработка поможет избежать подобных ситуаций.

    В примере ниже если запустить ячейку и прервать ядро, программа вызовет исключение KeyboardInterrupt. Теперь обработаем исключение KeyboardInterrupt.

    try:
        inp = input()
        print('Нажмите Ctrl+C и прервите Kernel:')
    except KeyboardInterrupt:
        print('Исключение KeyboardInterrupt')
    else:
        print('Исключений не произошло')
    
    
    Исключение KeyboardInterrupt
    

    Стандартные ошибки (StandardError)

    Рассмотрим некоторые базовые ошибки в программировании.

    Арифметические ошибки (ArithmeticError)

    • Ошибка деления на ноль (Zero Division);
    • Ошибка переполнения (OverFlow);
    • Ошибка плавающей точки (Floating Point);

    Все перечисленные выше исключения относятся к классу Arithmetic и вызываются при ошибках в арифметических операциях.

    Деление на ноль (ZeroDivisionError)

    Когда делитель (второй аргумент операции деления) или знаменатель равны нулю, тогда результатом будет ошибка деления на ноль.

    try:  
        a = 100 / 0
        print(a)
    except ZeroDivisionError:  
        print("Исключение ZeroDivisionError." )
    else:  
        print("Успех, нет ошибок!")
    
    Исключение ZeroDivisionError.
    

    Переполнение (OverflowError)

    Ошибка переполнение вызывается, когда результат операции выходил за пределы диапазона. Она характерна для целых чисел вне диапазона.

    try:  
        import math
        print(math.exp(1000))
    except OverflowError:  
        print("Исключение OverFlow.")
    else:  
        print("Успех, нет ошибок!")
    
    Исключение OverFlow.
    

    Ошибка утверждения (AssertionError)

    Когда инструкция утверждения не верна, вызывается ошибка утверждения.

    Рассмотрим пример. Предположим, есть две переменные: a и b. Их нужно сравнить. Чтобы проверить, равны ли они, необходимо использовать ключевое слово assert, что приведет к вызову исключения Assertion в том случае, если выражение будет ложным.

    try:  
        a = 100
        b = "PythonRu"
        assert a == b
    except AssertionError:  
        print("Исключение AssertionError.")
    else:  
        print("Успех, нет ошибок!")
    
    
    Исключение AssertionError.
    

    Ошибка атрибута (AttributeError)

    При попытке сослаться на несуществующий атрибут программа вернет ошибку атрибута. В следующем примере можно увидеть, что у объекта класса Attributes нет атрибута с именем attribute.

    class Attributes(obj):
        a = 2
        print(a)
    
    try:
        obj = Attributes()
        print(obj.attribute)
    except AttributeError:
        print("Исключение AttributeError.")
    
    
    2
    Исключение AttributeError.
    

    Ошибка импорта (ModuleNotFoundError)

    Ошибка импорта вызывается при попытке импортировать несуществующий (или неспособный загрузиться) модуль в стандартном пути или даже при допущенной ошибке в имени.

    import nibabel
    
    ---------------------------------------------------------------------------
    
    ModuleNotFoundError                       Traceback (most recent call last)
    
     in 
    ----> 1 import nibabel
    
    
    ModuleNotFoundError: No module named 'nibabel'
    

    Ошибка поиска (LookupError)

    LockupError выступает базовым классом для исключений, которые происходят, когда key или index используются для связывания или последовательность списка/словаря неверна или не существует.

    Здесь есть два вида исключений:

    • Ошибка индекса (IndexError);
    • Ошибка ключа (KeyError);

    Ошибка ключа

    Если ключа, к которому нужно получить доступ, не оказывается в словаре, вызывается исключение KeyError.

    try:  
        a = {1:'a', 2:'b', 3:'c'}  
        print(a[4])  
    except LookupError:  
        print("Исключение KeyError.")
    else:  
        print("Успех, нет ошибок!")
    
    
    Исключение KeyError.
    

    Ошибка индекса

    Если пытаться получить доступ к индексу (последовательности) списка, которого не существует в этом списке или находится вне его диапазона, будет вызвана ошибка индекса (IndexError: list index out of range python).

    try:
        a = ['a', 'b', 'c']  
        print(a[4])  
    except LookupError:  
        print("Исключение IndexError, индекс списка вне диапазона.")
    else:  
        print("Успех, нет ошибок!")
    
    Исключение IndexError, индекс списка вне диапазона.
    

    Ошибка памяти (MemoryError)

    Как уже упоминалось, ошибка памяти вызывается, когда операции не хватает памяти для выполнения.

    Ошибка имени (NameError)

    Ошибка имени возникает, когда локальное или глобальное имя не находится.

    В следующем примере переменная ans не определена. Результатом будет ошибка NameError.

    try:
        print(ans)
    except NameError:  
        print("NameError: переменная 'ans' не определена")
    else:  
        print("Успех, нет ошибок!")
    
    NameError: переменная 'ans' не определена
    

    Ошибка выполнения (Runtime Error)

    Ошибка «NotImplementedError»
    Ошибка выполнения служит базовым классом для ошибки NotImplemented. Абстрактные методы определенного пользователем класса вызывают это исключение, когда производные методы перезаписывают оригинальный.

    class BaseClass(object):
        """Опередляем класс"""
        def __init__(self):
            super(BaseClass, self).__init__()
        def do_something(self):
    	# функция ничего не делает
            raise NotImplementedError(self.__class__.__name__ + '.do_something')
    
    class SubClass(BaseClass):
        """Реализует функцию"""
        def do_something(self):
            # действительно что-то делает
            print(self.__class__.__name__ + ' что-то делает!')
    
    SubClass().do_something()
    BaseClass().do_something()
    
    
    SubClass что-то делает!
    
    
    
    ---------------------------------------------------------------------------
    
    NotImplementedError                       Traceback (most recent call last)
    
     in 
         14
         15 SubClass().do_something()
    ---> 16 BaseClass().do_something()
    
    
     in do_something(self)
          5     def do_something(self):
          6         # функция ничего не делает
    ----> 7         raise NotImplementedError(self.__class__.__name__ + '.do_something')
          8
          9 class SubClass(BaseClass):
    
    
    NotImplementedError: BaseClass.do_something
    

    Ошибка типа (TypeError)

    Ошибка типа вызывается при попытке объединить два несовместимых операнда или объекта.

    В примере ниже целое число пытаются добавить к строке, что приводит к ошибке типа.

    try:
        a = 5
        b = "PythonRu"
        c = a + b
    except TypeError:
        print('Исключение TypeError')
    else:
        print('Успех, нет ошибок!')
    
    
    Исключение TypeError
    

    Ошибка значения (ValueError)

    Ошибка значения вызывается, когда встроенная операция или функция получают аргумент с корректным типом, но недопустимым значением.

    В этом примере встроенная операция float получат аргумент, представляющий собой последовательность символов (значение), что является недопустимым значением для типа: число с плавающей точкой.

    try:
        print(float('PythonRu'))
    except ValueError:
        print('ValueError: не удалось преобразовать строку в float: 'PythonRu'')
    else:
        print('Успех, нет ошибок!')
    
    ValueError: не удалось преобразовать строку в float: 'PythonRu'
    

    Пользовательские исключения в Python

    В Python есть много встроенных исключений для использования в программе. Но иногда нужно создавать собственные со своими сообщениями для конкретных целей.

    Это можно сделать, создав новый класс, который будет наследовать из класса Exception в Python.

    class UnAcceptedValueError(Exception):   
        def __init__(self, data):    
            self.data = data
        def __str__(self):
            return repr(self.data)
    
    Total_Marks = int(input("Введите общее количество баллов: "))
    try:
        Num_of_Sections = int(input("Введите количество разделов: "))
        if(Num_of_Sections < 1):
            raise UnAcceptedValueError("Количество секций не может быть меньше 1")
    except UnAcceptedValueError as e:
        print("Полученная ошибка:", e.data)
    
    
    Введите общее количество баллов: 10
    Введите количество разделов: 0
    Полученная ошибка: Количество секций не может быть меньше 1
    

    В предыдущем примере если ввести что-либо меньше 1, будет вызвано исключение. Многие стандартные исключения имеют собственные исключения, которые вызываются при возникновении проблем в работе их функций.

    Недостатки обработки исключений в Python

    У использования исключений есть свои побочные эффекты, как, например, то, что программы с блоками try-except работают медленнее, а количество кода возрастает.

    Дальше пример, где модуль Python timeit используется для проверки времени исполнения 2 разных инструкций. В stmt1 для обработки ZeroDivisionError используется try-except, а в stmt2if. Затем они выполняются 10000 раз с переменной a=0. Суть в том, чтобы показать разницу во времени исполнения инструкций. Так, stmt1 с обработкой исключений занимает больше времени чем stmt2, который просто проверяет значение и не делает ничего, если условие не выполнено.

    Поэтому стоит ограничить использование обработки исключений в Python и применять его в редких случаях. Например, когда вы не уверены, что будет вводом: целое или число с плавающей точкой, или не уверены, существует ли файл, который нужно открыть.

    import timeit
    setup="a=0"
    stmt1 = '''
    try:
        b=10/a
    except ZeroDivisionError:
        pass'''
    
    stmt2 = '''
    if a!=0:
        b=10/a'''
    
    print("time=",timeit.timeit(stmt1,setup,number=10000))
    print("time=",timeit.timeit(stmt2,setup,number=10000))
    
    
    time= 0.003897680000136461
    time= 0.0002797570000439009
    

    Выводы!

    Как вы могли увидеть, обработка исключений помогает прервать типичный поток программы с помощью специального механизма, который делает код более отказоустойчивым.

    Обработка исключений — один из основных факторов, который делает код готовым к развертыванию. Это простая концепция, построенная всего на 4 блоках: try выискивает исключения, а except их обрабатывает.

    Очень важно поупражняться в их использовании, чтобы сделать свой код более отказоустойчивым.

    Понравилась статья? Поделить с друзьями:
  • Error messages generator
  • Error messages generated by commands are sent where by default stdin stdout log files stderr
  • Error messages for windows
  • Error messages examples
  • Error messages error 1407742e