What are exceptions

Errors are repulsive coding and they happen for many reasons.

Two types of error

  1. Syntax errors : which are caused by human error
  2. Exceptions : which are known errors that need to be handled

Syntax Errors

Syntax errors are usually caused by the developer. It could be the result of a misspelling or a typo in the code. Generally, these types of errors have minimal impact because most IDEs, like Visual Studio code, will warm the developer and give clues about how to fix them.

A common error for new developers learning in Python is not adding the colon at the end of conditions or statements. If you’re using a code editor with syntax checking, errors like this may be highlighted at the point of the error.

Examples

For example, a missing colon will be highlighted in the code. The output will indicate the filename and the line where the error occurred with, a [inaudible] pointing to the error. Running the code will result in an invalid syntax error in forming that there is a syntax problem.

Other common mistakes include indentation problems, which are also Syntax Errors. For example, if there is an indentation problem, the error code will be indentation error.

Exception Errors

They happen during code execution and they can easily go unnoticed by the untrained eye, but exceptions needs to be handled by the developer. They need to deal with any potential issues and the CodeBase to keep the application from failing.

As an example, your code can be syntactically correct, but if it attempts to divide five by zero, it doesn’t make mathematical sense. Therefore, when you run this program, ZeroDivisionError exception is thrown.

Luckily, by default, Python includes many exception errors that you can use to pick up potential issues in your code.


Exception handling

def divide_by(a, b):
    return a / b
 
print(divide_by(40, 0))
Traceback (most recent call last):
  File "PATH", line 4, in <module> 
    print(divide_by(40, 0))
          ^^^^^^^^^^^^^^^^
  File "PATH", line 2, in divide_by
    return a / b
           ~~^~~
ZeroDivisionError: division by zero
def divide_by(a, b):
    return a / b
 
try:
    print(divide_by(40, 0))
 
except:
    print("Something went wrong!")
Something went wrong!

The try statement will try and execute the code that you added inside it. If an exception occurs, it will trigger the except line and execute any code added underneath the except statement, but Python allows you to make the except statement more specific.

If you want to trap the exception itself you could add the base class exception right after accept. The base class exception is used for all exceptions that are written within Python. You can gain access to the exception information by using the as E after exception.

The E variable acts as an alias for the exception. I can use E to print out the exception in the print statement.

def divide_by(a, b):
    return a / b
 
try:
    print(divide_by(40, 0))
 
except Exception as e:
    print("Something went wrong!", e)
Something went wrong! division by zero

In Python, you can also get access to the actual type or class of exception that’s occurred.

def divide_by(a, b):
    return a / b
 
try:
    print(divide_by(40, 0))
 
except Exception as e:
    print("Something went wrong!", e)
    print(e.__class__)
Something went wrong! division by zero
<class 'ZeroDivisionError'>
def divide_by(a, b):
    return a / b
 
try:
    print(divide_by(40, 0))
except ZeroDivisionError as e:
    print(e, "we cannot divide by zero")
except Exception as e:
    print(e, "something went wrong")
division by zero we cannot divide by zero
def divide_by(a, b):
    return a / b
 
try:
    print(divide_by(40, 'e'))
except ZeroDivisionError as e:
    print(e, "we cannot divide by zero")
except Exception as e:
    print(e, "something went wrong")
unsupported operand type(s) for /: 'int' and 'str' something went wrong

Exercise: Exceptions in Python

Exercise

Your task in this exercise is to add code to trap exceptions and handle them in a more user-friendly way. 

IndexError

The below code has one problem. It is trying to locate an item from the list that does not exist. Running the code will throw the IndexError. Add exception handling to stop the error from being thrown and return a more user-friendly message such as “Item does not exist in the list”.

# Starter code
items = [1,2,3,4,5]
item = items[6]
print(item)

ZeroDivisionError

In math there are rules about dividing by zero. The below code is trying to do just that and will throw a ZeroDivisionError. Add exception handling to return back 0 instead of allowing the error to be thrown.

# Starter code
def divide_by(a, b):
    return a / b
 
 
ans = divide_by(40, 0)
print(ans)

FileNotFoundError

The code below is looking for a file that does not exist. Add exception handling to catch this error and return the following “The file could not be found”.

# Starter code
with open('file_does_not_exist.txt', 'r') as file:
    print(file.read())

Exceptions in Python - solution

Solution code

Task 1 : IndexError

try:
    item = items[6]
    print(item)
except: 
    print("The index does not exist in the list.")

Task 2 : ZeroDivisionError

def divide_by(a, b):
    try:
        return a / b
    except ZeroDivisionError:
        return 0
    except Exception as e:
        print(e, 'Something went wrong!')
 
ans = divide_by(10,0)
print(ans)

Task 3 : FileNotFoundError

try:
    with open('file_does_not_exist.txt', 'r') as file:
        print(file.read())
except:
    print("Unable to locate file")  

File handling in Python

As a developer, you’ll probably work with large amounts of data and far handling makes that easier. This is why it’s important to learn how to work with files. Whether you’re working with data on your computer, on the web or in the cloud, it will most likely be saved in some type of file.

There are two file handling functions in Python,

  1. open
  2. close

open()

The open function is used for reading, writing and creating files.

The open function accepts two arguments. The first is the file name and or the file location and the second argument is the mode.

open(<FILE_NAME> <FILE_LOCATION>, <MODE>)

The mode indicates what action is required such as reading, writing or creating.

It’s also specifies if you want the file output in text or binary format.

Modes

  • 'r' : which is used to open and read a file in text format
  • 'rb' : opens and reads a file in binary format
  • 'r+' : opens the file for both reading and writing
  • 'w' : opens the file for writing

WARNING

Note that 'w' will overwrite the existing file.

  • 'a' : opens files for editing or appending data.

close()

The close function is used for closing the open file connection.

NOTE

Know that it does not take any arguments.

with open()

There is one more way to open and close a file in Python and that’s with open function.

The advantage of using it is that it closes the file automatically.

text & binary

In Python, you generally handle files in two ways, either in text or binary format.

The text format is more user friendly because humans can read it.

You won’t be able to read files in binary formats, but they are much more compact and therefore result in better performance.

Python uses text as the default format for file handling. So just passing in any mode for reading or writing a file will automatically set it to a text format.

To set the file handling to binary, you need to pass the letter b along with either the read or write option.

Examples

# test.txt
 
Hello there!
file = open('test.txt', mode = 'r')
 
data = file.readline()
 
print(data)
 
file.close() 
Hello there!

readline will just return the first line of the file while readlines will output an array with multiple lines.

NOTE

The with open function is better at exception handling and will automatically close the file for you.

with open('test.txt', mode = 'r') as file:
    data = file.readline()
	
    print(data)
Hello there!

Creating Files

Files are used to permanently stored data. Anything stored in the variables of your code exists in random access memory or RAM. Since RAM loses its data when the computer is turned off, it’s important to be able to create files, so data is available for future use or as a permanent record.

In Python, we can create new files using the open function and enabling the write mode.

Examples

# with open('newfile.txt', mode='w') as file:
with open('newfile.txt', 'w') as file:
    file.write("This is a new file created!")

If I choose to write multiple lines of contents of the file instead of a single line, I can use the writelines function. The writelines function accepts a list.

with open('newfile.txt', 'w') as file:
    file.writelines(["This is a new file created!", "This is another line to be added."])
This is a new file created!This is another line to be added.

One thing to note is that every time I run my script, it’s replacing the current file.

On the other hand, if I want to add to the file as opposed to replacing it each time, I just need to change the action of mode. To do so, I replace the letter w and put in a letter a, which stands for append.

Always keep in mind the necessity to deal with any exception by using the try and accept statements.

try:
    with open('newfile.txt', 'w') as file:
        file.writelines(["This is a new file created!", "This is another line to be added."])
except FileNotFoundError as e:
    print("Error", e)

Reading Files

read()

The read method returns the entire contents of the file as a string that will contain all the characters.

You can also pass in an integer to return only the specified number of characters in the file.

readline()

The readline function returns a single line as a string.

The readline function can also include an integer argument for returning a specified number of characters on a single line.

readlines()

The readlines method reads the entire contents of the file and then returns it in an ordered list. This allows you to iterate over the list or pick out specific lines based on a condition.

Files are stored in directories and they have paths. Reading files from the same directory is straightforward. You only need the name of the file.

When working with different locations however, it’s important that you know the difference between absolute and relative paths.

Absolute paths

Absolute paths contain leading forward/, or drive label. An absolute file path includes all the information you need to locate a file, whether you are in that files directory or not.

  • /user/local/etc/somefile.txt
  • C:\users\system\somefile.txt

Relative paths

Relative paths normally don’t contain any reference to the root directory and are normally relative to the calling file. A relative file path only includes the information you need to locate a file in your current working directory.

  • somefile.txt
  • ./somefile.txt

Storing file contents in data structures

Imagine you are trying to come up with a name for your new dog. You’re really unsure of what you’d like to call the dog, so you decide to use your Python skills to help you decide.

You start by accessing a file with a shortlist of names you’d like to use for your new pet.

The file is named petnames.txt, and has the following content:

Ace
Atlas
Bailey
Bear
Blaze
Boomer
Buddy
Coco
Cooper
Duke
Dozer
Echo
Gizmo
Harley
Mac
Max
Milo
Oscar
Rex
Rocky
Rocket
Wolfie

Now that you have the file petnames.txt, you’d like to use this file inside your Python program to randomly pick a single pet name.

To do this, you’ll need to have a Python file into which you’ll be importing the petnames.txt file, as follows:

f = open("petnames.txt", "r")

The open() function reads in a file outside of the program itself.

The open() function accepts two parameters:

  1. The path and the filename in the form of a string.
  2. The import mode (the default being "r" - which means “read”)

In the line above, I am importing the file at the root of the project. I am only specifying the filename, without the path. I I am also using the default "r" mode to read in the file. I am saving the imported file into a variable named f.

Next, I’m going to add another variable, f_content, and I’m assigning to it the result of reading the f file.

On the third line, I’m printing the f_content variable.

The print(f_content) line returns the exact content of the file, as is:

Ace
Atlas
Bailey
Bear
Blaze
Boomer
Buddy
Coco
Cooper
Duke
Dozer
Echo
Gizmo
Harley
Mac
Max
Milo
Oscar
Rex
Rocky
Rocket
Wolfie

Now that I’ve confirmed that I’m successfully reading in the file, it would not be useful to keep printing out the file’s contents, so I can comment out the print(f_content) line.

Additionally, I can get the f_content variable into a list. The string "\n" is used to split the text where a new line is found.

f_content_list = f_content.split("\n")

Now I’m ready to print the f_content_list variable, as follows:

print(f_content_list)

This time, the output is as follows:

['Ace', 'Atlas', 'Bailey', 'Bear', 'Blaze', 'Boomer', 'Buddy', 'Coco', 'Cooper', 'Duke', 'Dozer', 'Echo', 'Gizmo', 'Harley', 'Mac', 'Max', 'Milo', 'Oscar', 'Rex', 'Rocky', 'Rocket', 'Wolfie']

Here’s my complete code up to this point, with the redundant print() calls deleted.

f = open("petnames.txt", "r")
f_content = f.read()
f_content_list = f_content.split("\n")
f.close()

Now that I have all my potential pet names in a list, I can randomly pick a name from the f_content_list of names.

To do this, I’ll need to import the random module at the top of my code: import random.

Now I can use the random module’s choice() function: random.choice().

The choice() function accepts a sequence parameter. A list is one of its accepted values. This means that I can now add another line of code at the very bottom of my program:

print(random.choice(f_content_list))

Running the code now will output a random pet name. The first time I ran it, I got the name “Milo”, and the second time I ran it, I got the name “Dozer”. It’s always good to double-check your programs like this by running them multiple times as a quick way to confirm that they behave as intended.

Here’s the full code of my program now (including the commented-out lines of code):

import random
f = open("petnames.txt", "r")
f_content = f.read()
f_content_list = f_content.split("\n")
f.close()
print(random.choice(f_content_list))

Finally, If I had multiple files in my folder, I could allow myself to pick a file from which to read in a list of names.

Here’s how that would work:

import random
f_name = input('Type the file name: ')
f = open(f_name) # "r" omitted as it's the default
f_content = f.read()
f_content_list = f_content.split("\n")
f.close()
print(random.choice(f_content_list))

The only difference between this improvement and the previous program is that now I’m saving the f_name variable as the result of user-provided input. Once I have the f_name variable, I’m running the open() function on it and then proceeding with other steps as already explained. Finally, the file is closed with the f.close() statement.


Additional resources

The following resources will be helpful as additional references in dealing with different concepts related to the topics you have covered in this module.

Learn more about exceptions and errors in Python on the Python website: Exceptions and Errors in Python - Python docs

Check out the PyNative website to learn more about file handling in Python: File handling in Python


FilesErrorExceptionPython

Previous one → 3.Functions and Data Structures | Next one → 5.Procedural programming