What we're doing today
By the end of this session, you will have made a program that writes to a file, opens it again later, and reads what it wrote. You will also know what to do when Python complains instead of doing what you asked. After that, we move off Colab and get Python running on your own laptop.
This lesson has more new words than the last one. Don't try to memorise them. Read, type, run, watch. The words stick on their own after the third or fourth time you see them.
Why files even matter
Everything you have written so far forgets itself. You type a name into input(), the program greets you, you close the cell, and the name is gone. That is fine for a greeting. It is not fine for anything real.
A file is a stored thing on a disk that survives the program ending. Think of a paper notebook. You write a few entries in it, you close the cover, you put it on a shelf. Next week you take it down, open it to the same page, and your entries are still there. That is exactly what a file is to a program. The program writes into the notebook, the program closes, and the entries wait quietly on the disk until something opens the notebook again.
Without files, every program is amnesia in code form. With files, your todo list still has yesterday's tasks in it. Your attendance log still has Monday's names.
Making a file to play with
Open a Colab notebook. On the left side of the screen there is a folder icon — click it. That opens the file pane. Right-click in the empty space and choose "New file". Call it todo.txt. Double-click to open it, and type three lines:
buy bread
call mum
finish assignmentSave it. Now we have a file sitting next to our code, with three lines in it, waiting to be read.
Opening the file from Python
In a Colab cell, type this and run it:
with open("todo.txt") as f:
contents = f.read()
print(contents)You should see your three lines printed back. Good. Now let me unpack what happened, because every piece of those two lines matters.
open("todo.txt") is how you ask Python to open the file. You give it the name in quotes, the same way you give text to print. Python finds the file on the disk and hands you back a way to talk to it.
The with part is a with block — a polite way to open a file that closes it again automatically when you're done. When you open a file, the operating system keeps a little channel open for you. If you forget to close it, that channel stays open and bad things slowly pile up. The with block guarantees the file gets closed the moment the indented code below it finishes, even if something goes wrong in the middle. Always use with when opening files.
as f gives the open file a short name so we can refer to it inside the block. We could have called it anything, but f is the convention.
f.read() pulls the contents out. It reads the whole file from top to bottom and gives it back as one big string of text.
Reading one line at a time
.read() works fine for a short todo list. For anything longer — say a log file with a thousand entries — pulling the whole thing into memory at once is wasteful. There is a better way:
with open("todo.txt") as f:
for line in f:
print("Task:", line)for line in f walks through the file one line at a time, top to bottom. On the first loop, line is "buy bread". On the second, "call mum". And so on.
You will notice extra blank lines between tasks in the output. Every line in a text file ends with an invisible newline character — that's what makes it a new line — and print adds its own newline on top. To strip that off:
with open("todo.txt") as f:
for line in f:
clean = line.strip()
print("Task:", clean).strip() removes whitespace from the start and end of a string, including that invisible newline. Now the output looks tidy.
Writing a file
Reading is half the story. To write, you pass a second piece of information to open — the mode.
tasks = ["buy bread", "call mum", "finish assignment"]
with open("todo.txt", "w") as f:
for task in tasks:
f.write(task + "\n")The "w" is the mode. It stands for write. And here is the part that catches everyone the first time: opening a file with "w" erases everything that was in the file before you write a single character. It is not adding to your todo list. It is tearing out all the pages of the notebook and starting fresh.
If you want to add a new entry to the end without losing the old ones, use "a" for append:
with open("todo.txt", "a") as f:
f.write("buy airtime\n")Append vs write is the difference between adding a new entry to the bottom of the page and tearing out all the pages and starting over. Pick the wrong one and you can wipe out an hour of work with one cell. Pause for a second before you type the mode.
The + "\n" at the end of each write is how you tell Python "and now move to a new line." Without it, all your tasks would land glued together on one line: buy breadcall mumfinish assignment. The \n is the newline character we just talked about — you have to put it in yourself when you write.
The three modes you will use this term:
"r"— read. The default. If you don't pass a mode, this is what you get."w"— write. Erases the file first, then writes."a"— append. Adds to the end. Leaves the existing contents alone.
When Python can't do what you asked
So far we have assumed the file is there, readable, and contains what we expect. The real world is not that polite. The file might not exist. The user might have typed the name wrong. The contents might be garbled. When Python runs into one of these problems, it does not silently keep going — it raises an exception.
An exception is what Python raises when it can't do what you asked. Think of an ATM that politely says "card declined" instead of swallowing your card and walking away. Python stops, tells you exactly what went wrong, and waits for you to handle it. That message is called a traceback — the trail Python prints when something breaks, showing you which line of code blew up and why.
Try this in a cell:
with open("does_not_exist.txt") as f:
print(f.read())You will see a red error like FileNotFoundError: [Errno 2] No such file or directory: 'does_not_exist.txt'. That is the exception. The program stopped. Nothing after that line ran.
Exceptions sound like a problem, but they are a gift. If Python silently gave you an empty string when the file didn't exist, you would build a whole program on top of that empty string and never know why it gave wrong answers. The exception stops you immediately so you can fix the cause.
Catching an exception
You handle exceptions with a try / except block. It looks like this:
try:
with open("todo.txt") as f:
tasks = f.read().splitlines()
except FileNotFoundError:
tasks = []
print(tasks)Read it out loud. Try this block. If a FileNotFoundError happens, run the except block instead.
The first time you run a todo list program, the file does not exist yet. Without the try, the program crashes on line one. With it, the program quietly notices the file is missing, starts with an empty list, and keeps going. The user sees a working program, not a red traceback.
.splitlines() is a small helper that takes one big string and splits it into a list of lines, with the newlines stripped off — handy for loading a file into a list in one step.
One small rule that will save you grief: catch the specific exception you expect, not a catch-all. except FileNotFoundError is good — you are saying "I expect this one particular thing to maybe go wrong, and here is what to do." A bare except: on its own catches every error in the universe, including bugs in your own code that you needed to see. It is a security guard who waves everyone through. Don't.
Moving off Colab
Right now your files live inside Colab, and Colab files vanish when the runtime disconnects. That is fine for practice. It is not fine if you want a todo list that is still there tomorrow.
So the second half of today is about installing Python on your own laptop. After this, you can write .py files, run them from a terminal, and the files sit on your hard drive like any other document.
The short version:
- Install Python 3.11 or newer from python.org.
- Install VS Code and add the Python extension.
- Open a terminal and run
python --versionto confirm Python is installed. - Make a folder called
pysprout. Inside it, create a file calledhello.pycontaining one line:
print("Hello from my own machine.")- In the terminal, change into that folder and run:
python hello.pyIf you see Hello from my own machine. print out, you have local Python. Then redo today's todo example in the same folder: create todo.txt next to hello.py, copy your reading code into a new .py file, and run it. Notice how much calmer everything is. No uploading. No vanishing. Just code and files sitting next to each other in one folder.
If the install fights you today, keep using Colab. We will sort it out together later.
Your exercise
Write a small program that keeps a list of tasks in a file called todo.txt. It should:
- Try to open
todo.txtand load the tasks into a list. If the file doesn't exist, start with an empty list. - Ask the user for one new task using
input(). - Add the new task to the list.
- Write the whole list back to
todo.txt, one task per line. - Print the full list at the end.
Run it twice. The second time, your first task should still be there, and the second task should join it.
Common slip-ups
- Using
"w"when you meant"a". You added one task and silently erased the other twenty. - Forgetting the
\nwhen writing. Everything ends up on one long line. - Forgetting
.strip()when reading. Your tasks all have invisible newlines stuck to them and weird comparisons fail. FileNotFoundErrorwhen the file is right there. Python is looking in a different folder than you think. In Colab, files live in/content/. Locally, files need to be in the same folder you ranpythonfrom.- A bare
except:that hides real bugs. Catch the specific exception you expect. - Skipping
with. The file stays open, and on some systems your writes don't actually hit the disk until the program ends.