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 thejson
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 creatingsave
andload
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 byfilename
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.
Load: When the program starts, instead of setting
inventory = []
, we must callload_inventory_from_file()
.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.