Skip to content

Contact sales

By filling out this form and clicking submit, you acknowledge our privacy policy.
  • Labs icon Lab
  • A Cloud Guru
Google Cloud Platform icon
Labs

Reading and Writing Files with Python

Files are used for many things in programming, including storing and reading data as well as writing to the screen. In this hands-on lab, we'll add a way to read and store information about custom classes, using a file as a flat database for our employee information. To feel comfortable completing this lab, you'll want to know how to read and write to files (watch the "Interacting with Files" video from the Certified Associate in Python Programming Certification course), use class methods (watch the "Custom Constructors, Class Methods, and Decorators" video from the Certified Associate in Python Programming Certification course), and create and use class instances (watch the "Creating and Using Python Classes" video from the Certified Associate in Python Programming Certification course).

Google Cloud Platform icon
Labs

Path Info

Level
Clock icon Intermediate
Duration
Clock icon 45m
Published
Clock icon Mar 20, 2020

Contact sales

By filling out this form and clicking submit, you acknowledge our privacy policy.

Table of Contents

  1. Challenge

    Add `identifier` Attribute to `Employee` Instances and `__init__` Method

    Before we start reading our Employee data from a file, we're going to add an additional field to help us identify our employees so we can update them later. We're going to call this field identifier, and we need to add it to the __init__ method as the final parameter with a default value of None. Here's what our method will look like in our employee.py:

    ~/employee.py

    class Employee:
        def __init__(self, name, email_address, title, phone_number=None, identifier=None):
            self.name = name
            self.email_address = email_address
            self.title = title
            self.phone_number = phone_number
            self.identifier = identifier
    
        def email_signature(self, include_phone=False):
            signature = f"{self.name} - {self.title}\n{self.email_address}"
            if include_phone and self.phone_number:
                signature += f" ({self.phone_number})"
            return signature
    

    Now we can differentiate instances when reading and writing from a file.

  2. Challenge

    Add `Employee.get_all` Class Method to Return a List of `Employee` Objects

    The first class method we're going to write will read in all of the employees from a file, with each line being a single employee. Here's a list of all the things we need to do:

    1. Determine the file to read from. If no filename is given, we'll use a default file name of employee_file.txt.
    2. Open the file, read each line, split the values on a , character, and add an additional value to the list that is the line number of the employees' data.
    3. Create a new Employee instance using the data. The data will be stored in the same order as the parameters so we can unpack the data from the file as positional arguments using the * operator.
    4. Return the list of Employee objects.

    Here's what this will look like:

    ~/employee.py

    class Employee:
        default_db_file = "employee_file.txt"
    
        @classmethod
        def get_all(cls, file_name=None):
            results = []
    
            if not file_name:
                file_name = cls.default_db_file
    
            with open(file_name, "r") as f:
                lines = [
                    line.strip("\n").split(",") + [index + 1]
                    for index, line in enumerate(f.readlines())
                ]
    
            for line in lines:
                results.append(cls(*line))
    
            return results
    
        # remainder of class was unchanged and omitted
    

    Because each line is going to have a \n character at the end, we're going to remove that before we split the line into its values. Additionally, when we think of line numbers, we start counting at 1 instead of 0, so we're going to make the identifier begin at 1.

  3. Challenge

    Add `Employee.get_at_line` Class Method to Return a Single `Employee`

    The get_at_line class method won't be much different than get_all, except we want to return a single value. We're going to need to take the line_number provided as an argument and subtract 1 from it so it can be used as an index of the list of lines. Here's one way we could implement this function:

    class Employee:
        default_db_file = "employee_file.txt"
    
        @classmethod
        def get_all(cls, file_name=None):
            results = []
    
            if not file_name:
                file_name = cls.default_db_file
    
            with open(file_name, "r") as f:
                lines = [
                    line.strip("\n").split(",") + [index + 1]
                    for index, line in enumerate(f.readlines())
                ]
    
            for line in lines:
                results.append(cls(*line))
    
            return results
    
        @classmethod
        def get_at_line(cls, line_number, file_name=None):
            if not file_name:
                file_name = cls.default_db_file
    
            with open(file_name, 'r') as f:
                line = f.readlines()[line_number - 1]
                attrs = line.strip("\n").split(',') + [line_number]
                return cls(*attrs)
    
        # remainder of class was unchanged and omitted
    
  4. Challenge

    Add `save` Instance Method to `Employee` Class to Write New Instances to the File

    The last method we're going to add is an instance method that will allow us to have an instance update or add its own in the "database" file. This method will need to do a few things. To make it more manageable to see what is going on, we're going to place the logic that builds the line we'll insert into the file into a separate "private" method (starting with a single underscore). Here's what we need this method to do:

    1. Determine the file to save to, defaulting to the default_db_file value if no file_name is passed in.
    2. Open the database file in r+ mode so we don't delete its contents if there are some.
    3. If the instance has an identifier, replace that line by getting all lines in a list and then replacing the appropriate index.
    4. If the instance does not have an identifier, add the line for this employee to the end of the list.
    5. seek back to the beginning of the file and use writelines to put all the lines back in the file.

    Our method to create the line we write into the file will be called _database_line. Here are both of the methods:

    ~/employee.py

    class Employee:
        default_db_file = "employee_file.txt"
    
        @classmethod
        def get_all(cls, file_name=None):
            results = []
    
            if not file_name:
                file_name = cls.default_db_file
    
            with open(file_name, "r") as f:
                lines = [
                    line.strip("\n").split(",") + [index + 1]
                    for index, line in enumerate(f.readlines())
                ]
    
            for line in lines:
                results.append(cls(*line))
    
            return results
    
        @classmethod
        def get_at_line(cls, line_number, file_name=None):
            if not file_name:
                file_name = cls.default_db_file
    
            with open(file_name, "r") as f:
                line = [
                    line.strip("\n").split(",") + [index + 1]
                    for index, line in enumerate(f.readlines())
                ][line_number - 1]
                return cls(*line)
    
        def __init__(self, name, email_address, title, phone_number=None, identifier=None):
            self.name = name
            self.email_address = email_address
            self.title = title
            self.phone_number = phone_number
            self.identifier = identifier
    
        def email_signature(self, include_phone=False):
            signature = f"{self.name} - {self.title}\n{self.email_address}"
            if include_phone and self.phone_number:
                signature += f" ({self.phone_number})"
            return signature
    
        def save(self, file_name=None):
            if not file_name:
                file_name = self.default_db_file
    
            with open(file_name, "r+") as f:
                lines = f.readlines()
                if self.identifier:
                    lines[self.identifier - 1] = self._database_line()
                else:
                    lines.append(self._database_line())
                f.seek(0)
                f.writelines(lines)
    
        def _database_line(self):
            return (
                ",".join(
                    [self.name, self.email_address, self.title, self.phone_number or ""]
                )
                + "\n"
            )
    

    We can test our implementation by running test_employee.py. If the implementation is correct, we won't see any errors — but if things aren't working correctly, we will see error messages that can hopefully help us.

    python3.7 test_employee.py
    

The Cloud Content team comprises subject matter experts hyper focused on services offered by the leading cloud vendors (AWS, GCP, and Azure), as well as cloud-related technologies such as Linux and DevOps. The team is thrilled to share their knowledge to help you build modern tech solutions from the ground up, secure and optimize your environments, and so much more!

What's a lab?

Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.

Provided environment for hands-on practice

We will provide the credentials and environment necessary for you to practice right within your browser.

Guided walkthrough

Follow along with the author’s guided walkthrough and build something new in your provided environment!

Did you know?

On average, you retain 75% more of your learning if you get time for practice.

Start learning by doing today

View Plans