Python for Profit

Building a Complete Inventory System

06

Giving Your Program a Real Memory with Files

The Big Idea

This chapter gives our application a persistent memory by teaching it how to save the inventory data to a file before it closes and load it back when it starts, using Python's built-in json library.

Roadmap

  • The "Amnesia" Problem: We'll highlight why our program currently forgets everything as soon as it stops running.

  • Persistent Storage: Introduce the concept of saving data to a file on the computer's hard drive.

  • JSON: The Perfect Format for Python Data: Learn about JSON (JavaScript Object Notation), a simple, text-based format that is perfect for storing Python lists and dictionaries.

  • Writing to a File: Use the with open(...) statement and the json module to "serialize" our Python list into a JSON file.

  • Reading from a File: Learn how to "deserialize" the data from the JSON file back into a Python list our program can use.

  • Handling a Missing File: Implement a try...except block, a professional way to handle errors that occur when the program runs for the first time and the data file doesn't exist yet.

  • Building main.py Step-by-Step: We will upgrade our script by creating save and load functions and integrating them into our application's lifecycle.

Full Chapter Content

Solving Our Program's Amnesia

Our application is functional and well-organized, but it has a critical flaw: it has no long-term memory. Every time you run python main.py, the inventory list starts completely empty. Any products you add are stored in the computer's RAM (Random Access Memory), which is volatile. As soon as the program ends, the RAM is cleared, and all your hard work vanishes.

To make our tool useful, we need persistence. We need to save the state of our inventory list to a permanent storage medium—a file on the hard drive.

JSON: A Universal Language for Data

How should we store our Python list in a file? We could try to write it directly, but there's a much better, standardized way: JSON (JavaScript Object Notation). Despite its name, JSON is a language-independent, human-readable text format that has become the standard for data exchange on the web.

It's a perfect match for Python because its structure mirrors Python's lists and dictionaries.

A Python list like this: [["Laptop", 10, 1200.00], ["Mouse", 50, 25.00]]

Looks like this in JSON format: [["Laptop", 10, 1200.00], ["Mouse", 50, 25.00]]

They look almost identical! This makes converting between them incredibly easy using Python's built-in json module.

Building main.py Step-by-Step

Let's modify our application to include file operations.

Step 1: Import json and Define the Filename

First, we need to tell Python we want to use the json module. We do this with an import statement at the very top of the file. We'll also define the name of our data file in the configuration section.

Modify the top of your main.py file to look like this:

# --- PyInventory: A Step-by-Step Journey to Profit ---
# Chapter 6: Giving Your Program a Real Memory with Files

import json # Import the json module

# --- Configuration ---
CURRENCY = "MAD"
DATA_FILE = "inventory.json" # The name of our data file

# --- Data Store ---
# We will now load this from a file instead of starting empty.
inventory = []

Step 2: Define the "Save Inventory" Function

Now, let's create a function that handles saving our inventory list to the inventory.json file. We use the with open(...) syntax, which is the recommended way to work with files in Python because it automatically handles closing the file for you, even if errors occur.

Add this function to the "Function Definitions" section of your script:

def save_inventory_to_file(inventory_list, filename):
    """Saves the inventory list to a file in JSON format."""
    with open(filename, 'w') as file:
        json.dump(inventory_list, file, indent=4)
    print(f"Data saved to {filename}.")

  • open(filename, 'w'): This opens the file specified by filename in 'w'rite mode. If the file exists, it will be overwritten. If it doesn't exist, it will be created.

  • json.dump(...): This is the magic function. It takes your Python object (inventory_list) and a file object (file) and writes the data as a JSON formatted string into that file. indent=4 makes the file nicely formatted and human-readable.

Step 3: Define the "Load Inventory" Function

Next, we need a function to load the data when the program starts. This function needs to be robust. What happens on the very first run when inventory.json doesn't exist yet? Trying to open it would cause a FileNotFoundError and crash our program.

We handle this gracefully using a try...except block. Python will try to run the code in the try block. If a FileNotFoundError occurs, it will skip the rest of that block and run the code in the except block instead.

Add this function to your script:

def load_inventory_from_file(filename):
    """Loads the inventory list from a JSON file."""
    try:
        with open(filename, 'r') as file:
            return json.load(file)
    except FileNotFoundError:
        print(f"Welcome! '{filename}' not found. Starting with an empty inventory.")
        return [] # Return an empty list if the file doesn't exist

  • open(filename, 'r'): This opens the file in 'r'ead mode.

  • json.load(file): This reads the JSON data from the file and deserializes it back into a Python object (in our case, a list of lists).

  • return []: If the file isn't found, we simply return a new empty list, allowing our program to start cleanly.

Step 4: Integrate Saving and Loading into the Main Loop

Finally, we need to call our new functions at the right moments in the application's lifecycle.

  1. Load: When the program starts, instead of setting inventory = [], we must call load_inventory_from_file().

  2. Save: When the user chooses to quit, we must call save_inventory_to_file() before the loop breaks and the program ends.

Here is the final, modified main.py file. Replace your entire script with this content:

# --- PyInventory: A Step-by-Step Journey to Profit ---
# Chapter 6: Giving Your Program a Real Memory with Files

import json # Import the json module

# --- Configuration ---
CURRENCY = "MAD"
DATA_FILE = "inventory.json"

# --- Function Definitions ---

def save_inventory_to_file(inventory_list, filename):
    """Saves the inventory list to a file in JSON format."""
    with open(filename, 'w') as file:
        json.dump(inventory_list, file, indent=4)

def load_inventory_from_file(filename):
    """Loads the inventory list from a JSON file."""
    try:
        with open(filename, 'r') as file:
            return json.load(file)
    except FileNotFoundError:
        return [] # Return an empty list if the file doesn't exist

def display_inventory(inventory_list, currency_symbol):
    """Displays the current inventory in a formatted table."""
    # (This function's code remains exactly the same)
    print("\n--- Current Inventory Report ---")
    print("-" * 60)
    total_inventory_value = 0.0

    if not inventory_list:
        print("Inventory is currently empty.")
    else:
        # Adjusted print formatting for better alignment
        print(f"{'Product':<30} | {'Quantity':<10} | {'Value':>15}")
        print("-" * 60)
        for item in inventory_list:
            item_name, item_quantity, item_price = item
            item_value = item_quantity * item_price
            total_inventory_value += item_value
            print(f"{item_name:<30} | {item_quantity:<10} | {currency_symbol}{item_value:>14.2f}")

    print("-" * 60)
    print(f"GRAND TOTAL INVENTORY VALUE: {currency_symbol}{total_inventory_value:>40.2f}")
    print("-" * 60)


def add_product(inventory_list, currency_symbol):
    """Interactively asks for a new product and adds it to the list."""
    # (This function's code remains exactly the same)
    print("\n--- Add a New Product ---")
    product_name = input("Enter the new product name: ")
    quantity_str = input(f"Enter the quantity for {product_name}: ")
    price_str = input(f"Enter the price per item (in {currency_symbol}): ")

    new_product = [product_name, int(quantity_str), float(price_str)]

    inventory_list.append(new_product)
    print(f"\nSUCCESS: '{product_name}' has been added to the inventory.")

# --- Main Application Logic ---

# 1. Load the inventory from the file when the program starts.
inventory = load_inventory_from_file(DATA_FILE)

while True:
    print("\n--- PyInventory Main Menu ---")
    print("1: Display Current Inventory")
    print("2: Add a New Product")
    print("q: Quit")

    choice = input("Please enter your choice (1, 2, or q): ")

    if choice == '1':
        display_inventory(inventory, CURRENCY)
    elif choice == '2':
        add_product(inventory, CURRENCY)
    elif choice.lower() == 'q':
        # 2. Save the inventory to the file before quitting.
        save_inventory_to_file(inventory, DATA_FILE)
        print("Inventory saved. Exiting PyInventory. Goodbye!")
        break
    else:
        print("Invalid choice. Please try again.")

I've also taken the liberty of slightly improving the print formatting in display_inventory to create cleaner columns.

Now, run your program. The first time, it will start with an empty inventory. Add two or three products. Then, choose 'q' to quit. You will see a new file, inventory.json, appear in your project folder! Open it—your data is there. Run the program again. This time, it will load that data, and your products will be right where you left them.

Chapter 6: Summary & What's Next

You have just given your program one of the most critical features of any real application: a memory.

  • You learned how to use the json module to serialize and deserialize Python data.

  • You mastered the with open(...) syntax for safe file handling.

  • You implemented a try...except block to gracefully handle potential errors.

  • You successfully integrated loading and saving into your application's lifecycle.

Our data is now safe, but our data structure is still a bit basic. A list of lists works, but it can be hard to remember that index [0] is the name and [2] is the price. In the next chapter, we'll introduce dictionaries, a more powerful and descriptive way to structure our product data.