A student showed me her contacts app last week. She had spent two evenings on it. Names, numbers, a tidy little menu, the works. She ran it, added five contacts, closed the terminal, and ran it again. Empty. All of it gone.
She looked at me like the program had betrayed her. It hadn't. It had done exactly what we taught it to do: hold things in memory while it was running, and forget them the moment it stopped. Nobody had ever told it where to write the contacts down.
That's the gap I want to close in this post. Variables live inside a running program. The moment the program ends, that memory is wiped. If your scholarship tracker forgot every entry every time you closed it, you would never open it twice. To be useful, a program needs a place to write things down so it can read them again later. That place is a file.
A file is a notebook on disk
Think of a file the way you would think of a small notebook sitting next to you. You can open it, read what's there, write something new, close it, and come back to it tomorrow. Your laptop will keep the pages safe whether or not your program is running.
Here is the simplest possible read in Python:
with open("gpas.txt") as f:
for line in f:
print(line.strip())The piece worth slowing down on is with open(...) as f:. Read it as a polite request. You are asking Python to open the file and lend it to you for the length of the indented block. The moment the block ends — even if your code crashes inside it — Python closes the door behind you. No leaving the notebook open on the desk. No half-written lines that never make it to disk.
You'll see code online that just says f = open(...) and never closes it. Don't copy that habit. The with form is the version that doesn't bite you six months from now.
Append versus write
Writing has a sharp edge most beginners step on once, and only once.
with open("contacts.txt", "w") as f:
f.write("Amara,+233 24 000 0000\n")That "w" is the mode. It means open this file for writing. Sounds harmless. What it actually does is tear the pages out of the notebook first and then write your one line on page one. Everything that was in the file before is gone.
If you want to add a new line to what's already there, the mode you want is "a" for append:
with open("contacts.txt", "a") as f:
f.write("Diego,+1 415 555 0199\n")Append is jotting a new entry at the bottom of the page. Write is ripping the notebook in half and starting over. Both are useful. Pick on purpose. The day you mean to add one contact and accidentally erase forty is a day you only need once to remember the difference.
For the scholarship tracker you're building, my advice is to keep all your scholarships in a list while the program runs, and rewrite the whole file with "w" whenever the user saves. A clean snapshot is easier to reason about than a file that's been appended to a hundred times.
What an error actually is
Sooner or later your program will ask for something the world can't give it. The file isn't where you said it was. The user typed eighty when you needed a number. The internet was off when you tried to fetch a page.
Python's response to those moments is an exception. It's not a punishment, and it's not a sign you wrote bad code. It's a signal: I tried to do what you asked and I can't, here's why. If you ignore the signal, the program stops. If you catch the signal, you get to decide what happens next.
Think of an ATM. When your card is declined, the machine doesn't catch fire and shut down the bank. It says "card declined" and waits for you to try something else. That calm response is what we're after in our own programs.
try and except, the polite recovery
The tool Python gives you is try and except. Wrap the line that might fail in a try block, and say what to do if it does.
try:
with open("students.json") as f:
data = f.read()
except FileNotFoundError:
data = "[]"
print("Starting with:", data)Read that out loud. Try to open students.json and read it. If it isn't there, treat the data as an empty list instead. The program keeps moving either way. A first-time user, who has no saved file yet, gets a clean start. A returning user gets their data back. Same code, two outcomes, no crash.
One rule I'll repeat because it saves so much grief: catch the specific exception you expect. except FileNotFoundError is good. except ValueError is good. A bare except: that swallows everything is a security guard who lets all the bad news through without telling you. You don't want that. You want the guard who checks the one thing you asked them to check, and lets the real surprises through to your attention.
Take this with you
The shape of almost every small program you'll write from here on is the same: load whatever was saved last time, do some work, save it again. Files are the bridge between one run and the next. try and except are how you keep that bridge from collapsing the first time something is missing. Master those two patterns and your programs stop feeling like toys that reset every time you close them, and start feeling like tools you can actually use.