← Back to course

Files: Saving and Reading Data

Files: Saving and Reading Data

Welcome back.

In the previous lesson, you learned functions.

Your programs learned how to organize code into reusable blocks.

You wrote things like:

def greet_user(name):
    print(f"Hello, {name}!")

Very good.

Functions made your code cleaner.

Less repetition.

Less copy-paste chaos.

Less “why did I write this same thing seven times?”

Excellent progress.

But now we have another problem.

Your program can store data in variables.

Your program can store lists.

Your program can store dictionaries.

But when the program stops, everything disappears.

Very sad.

Very temporary.

Example:

tasks = ["Buy milk", "Study Python", "Drink coffee"]

This list exists while the program is running.

But when the program ends?

Gone.

Like your motivation after reading a 300-line error message.

Files solve this problem.

Files let your program save data.

Then your program can read that data later.

This is a big step.

Programs become much more useful when they can remember things.

Tasks.

Notes.

Contacts.

Settings.

Reports.

Logs.

Tiny memories.

Very useful.

Very Python.

What You Will Learn

In this lesson, you will learn:

By the end of this lesson, your programs will no longer forget everything immediately.

They will be able to save data.

Read data.

Append data.

Load data.

Very powerful.

Very practical.

Very “now this program is becoming useful”.

Why Files Matter

Variables live in memory.

Files live on disk.

A variable is temporary:

name = "Anna"

When the program ends, the variable disappears.

A file can stay after the program ends.

For example:

notes.txt
tasks.txt
contacts.txt
report.txt

Your program can write data into a file.

Later, the program can open the file and read the data.

This is important for many real programs.

Examples:

A task manager saves tasks.
A notes app saves notes.
A calculator saves history.
A game saves progress.
A website writes logs.
A script writes a report.

Without files, your program has a very short memory.

Like a goldfish with a keyboard.

Cute.

Not very useful.

Writing to a File

Create a file:

write_file.py

Write:

file = open("message.txt", "w")

file.write("Hello from Python!")

file.close()

Run it:

python write_file.py

or:

python3 write_file.py

Python creates a file called:

message.txt

Inside the file, you should see:

Hello from Python!

This line opens the file:

file = open("message.txt", "w")

This line writes text:

file.write("Hello from Python!")

This line closes the file:

file.close()

Very important:

Always close files if you open them manually.

But Python has a better way.

Because Python saw humans forgetting things and said:

Fine, I will help.

Using with open()

The better way is to use with open().

Example:

with open("message.txt", "w") as file:
    file.write("Hello from Python!")

This does the same thing.

But it automatically closes the file.

Much better.

Much safer.

Much less “oops, I forgot close()”.

Create a file:

write_file_with.py

Write:

with open("message.txt", "w") as file:
    file.write("Hello from Python with with open()!")

Run it.

Open message.txt.

You should see:

Hello from Python with with open()!

From now on, use:

with open(...)

This is the recommended style.

Cleaner.

Safer.

More professional.

Less likely to make Python sigh.

File Modes

When you open a file, you choose a mode.

The mode tells Python what you want to do.

Common modes:

"r"  read
"w"  write
"a"  append

Example:

open("file.txt", "r")

means:

Open file for reading.

Example:

open("file.txt", "w")

means:

Open file for writing.

Example:

open("file.txt", "a")

means:

Open file for appending.

Important warning:

"w" overwrites the file.

If the file already has content, "w" replaces it.

No mercy.

No drama.

Just gone.

Python with "w" mode is like:

New file. New life. Old content? Goodbye.

Be careful.

Writing Multiple Lines

You can write multiple lines using \n.

Example:

with open("notes.txt", "w") as file:
    file.write("First line\n")
    file.write("Second line\n")
    file.write("Third line\n")

The \n means:

new line

After running the program, notes.txt will contain:

First line
Second line
Third line

Without \n, everything stays on one line.

Example:

file.write("First")
file.write("Second")

Result:

FirstSecond

Not beautiful.

Not readable.

Very stuck together.

Use \n when you want a new line.

Tiny symbol.

Big formatting power.

Reading a File

Create a file called:

message.txt

Put this text inside:

Hello from a file!

Now create a Python file:

read_file.py

Write:

with open("message.txt", "r") as file:
    content = file.read()

print(content)

Run it.

Output:

Hello from a file!

This line opens the file for reading:

with open("message.txt", "r") as file:

This line reads the whole file:

content = file.read()

Then we print it:

print(content)

Very simple.

Very useful.

Your program just read data from disk.

Small moment.

Big power.

Reading Whole File vs Lines

read() reads the whole file.

Example:

with open("notes.txt", "r") as file:
    content = file.read()

This is fine for small files.

But sometimes you want to read line by line.

Example:

with open("notes.txt", "r") as file:
    for line in file:
        print(line)

If notes.txt contains:

First line
Second line
Third line

Output may look like:

First line

Second line

Third line

Why extra blank lines?

Because each line already has a newline at the end.

And print() adds another newline.

Very generous.

Too generous.

We can fix it with .strip().

Using strip()

.strip() removes extra whitespace from the beginning and end of a string.

It also removes newline characters.

Example:

text = "Hello\n"
clean_text = text.strip()

print(clean_text)

Output:

Hello

Use it when reading lines:

with open("notes.txt", "r") as file:
    for line in file:
        print(line.strip())

Now output is cleaner:

First line
Second line
Third line

.strip() is very useful when reading files.

Files often contain newlines.

Humans often do not want to see weird spacing.

Python gives you .strip().

Small tool.

Big relief.

Appending to a File

Write mode "w" overwrites a file.

Append mode "a" adds to the end.

Create a file:

append_file.py

Write:

with open("notes.txt", "a") as file:
    file.write("New note\n")

Run it multiple times.

Each time, Python adds:

New note

to the end of the file.

This is useful for:

logs
notes
tasks
history
reports

Append mode is safer when you do not want to delete old content.

Remember:

"w" writes from zero.
"a" adds to the end.

Very important.

Very file survival.

Writing User Input to a File

Create a file:

save_note.py

Write:

note = input("Write a note: ")

with open("notes.txt", "a") as file:
    file.write(note + "\n")

print("Note saved.")

Example:

Write a note: Study Python files

The program saves the note into:

notes.txt

If you run it again, it adds another note.

This is already useful.

A tiny note saver.

Not fancy.

Not cloud-based.

Not subscription.

Just a file.

Beautiful.

Reading Saved Notes

Create a file:

show_notes.py

Write:

with open("notes.txt", "r") as file:
    for line in file:
        print(f"- {line.strip()}")

If notes.txt contains:

Study Python files
Drink coffee
Build something useful

Output:

- Study Python files
- Drink coffee
- Build something useful

Now you have two programs:

save_note.py
show_notes.py

One saves notes.

One reads notes.

Tiny system.

Very humble.

Very useful.

The beginning of software.

With fewer login forms.

For now.

File Not Found Error

If you try to read a file that does not exist, Python gives an error.

Example:

with open("missing.txt", "r") as file:
    content = file.read()

Error:

FileNotFoundError

Why?

Because "missing.txt" does not exist.

Python cannot read a file that is not there.

Very logical.

Very annoying.

One simple way is to create the file first.

Another way is to handle the error.

We will learn error handling more deeply later.

For now, remember:

Reading requires the file to exist.
Writing can create a file.
Appending can create a file if it does not exist.

This is important.

Very practical.

Very “why did my script crash?” practical.

Saving a List to a File

Imagine we have a list:

tasks = ["Buy milk", "Study Python", "Clean desk"]

We can save each item on a separate line.

Example:

tasks = ["Buy milk", "Study Python", "Clean desk"]

with open("tasks.txt", "w") as file:
    for task in tasks:
        file.write(task + "\n")

The file tasks.txt will contain:

Buy milk
Study Python
Clean desk

This is simple and useful.

Each task becomes one line.

A list in Python becomes lines in a file.

Very clear.

Very beginner-friendly.

Very practical.

Loading a List from a File

Now we can read the file and rebuild the list.

Example:

tasks = []

with open("tasks.txt", "r") as file:
    for line in file:
        tasks.append(line.strip())

print(tasks)

Output:

['Buy milk', 'Study Python', 'Clean desk']

What happens?

Start with an empty list.
Open the file.
Read each line.
Remove newline with strip().
Append the clean text to the list.

Now the file became a Python list again.

This is very important.

This is how your program can remember data between runs.

Save list.

Close program.

Open program later.

Load list.

Memory achieved.

Tiny victory.

Building Helper Functions

We already know functions.

So we can organize file logic into functions.

Example:

def save_tasks(tasks):
    with open("tasks.txt", "w") as file:
        for task in tasks:
            file.write(task + "\n")

And:

def load_tasks():
    tasks = []

    with open("tasks.txt", "r") as file:
        for line in file:
            tasks.append(line.strip())

    return tasks

Now the main program can use:

tasks = load_tasks()
save_tasks(tasks)

This is cleaner.

Functions and files work well together.

Functions organize the behavior.

Files store the data.

Very nice.

Very real software energy.

Handling Missing File Simply

If the file does not exist, load_tasks() will fail.

We can handle it with try and except.

Do not panic.

This is just a simple version.

Example:

def load_tasks():
    tasks = []

    try:
        with open("tasks.txt", "r") as file:
            for line in file:
                tasks.append(line.strip())
    except FileNotFoundError:
        pass

    return tasks

What happens?

Try to open tasks.txt.
If it exists, load tasks.
If it does not exist, ignore the error.
Return an empty list.

This is useful.

The first time you run the program, the file may not exist yet.

That is normal.

The program should not explode dramatically.

This try and except keeps it calm.

Like Python yoga.

Mostly.

Mini Program: Save One Task

Create a file:

save_task.py

Write:

task = input("Task: ")

with open("tasks.txt", "a") as file:
    file.write(task + "\n")

print("Task saved.")

Run it.

Example:

Task: Study Python files

Run it again.

Example:

Task: Build a small app

Now tasks.txt contains:

Study Python files
Build a small app

This is already a useful little script.

Small.

Simple.

But it remembers.

And remembering is powerful.

Unless it remembers embarrassing variable names.

Then less powerful.

Mini Program: Show Tasks

Create a file:

show_tasks.py

Write:

try:
    with open("tasks.txt", "r") as file:
        print("Tasks:")

        for line in file:
            print(f"- {line.strip()}")
except FileNotFoundError:
    print("No tasks found.")

If the file exists, it shows tasks.

If the file does not exist, it prints:

No tasks found.

This is better than crashing.

Crashing is educational.

But not always friendly.

A good program handles simple problems.

A missing file is a simple problem.

Mostly.

Mini Program: Task Manager with Files

Now let us build a task manager that remembers tasks.

Create a file:

task_manager_files.py

Write:

FILE_NAME = "tasks.txt"

def load_tasks():
    tasks = []

    try:
        with open(FILE_NAME, "r") as file:
            for line in file:
                tasks.append(line.strip())
    except FileNotFoundError:
        pass

    return tasks

def save_tasks(tasks):
    with open(FILE_NAME, "w") as file:
        for task in tasks:
            file.write(task + "\n")

def show_menu():
    print("----- Task Manager -----")
    print("1. Add task")
    print("2. Show tasks")
    print("q. Quit")

def add_task(tasks):
    task = input("Task: ")
    tasks.append(task)
    save_tasks(tasks)
    print("Task saved.")

def show_tasks(tasks):
    if len(tasks) == 0:
        print("No tasks yet.")
    else:
        print("Tasks:")

        for task in tasks:
            print(f"- {task}")

tasks = load_tasks()

while True:
    show_menu()

    choice = input("Choose an option: ").lower()

    if choice == "1":
        add_task(tasks)
    elif choice == "2":
        show_tasks(tasks)
    elif choice == "q":
        print("Goodbye.")
        break
    else:
        print("Unknown option.")

This is a real improvement.

Before this, your task manager forgot everything.

Now it saves tasks to a file.

When the program starts, it loads tasks from the file.

When you add a task, it saves the list again.

This is real program behavior.

Very good.

Very practical.

Very “my code has memory now”.

How the Task Manager Works

The file name is stored here:

FILE_NAME = "tasks.txt"

This function loads tasks:

def load_tasks():

This function saves tasks:

def save_tasks(tasks):

This function adds a task:

def add_task(tasks):

Inside add_task, we do this:

tasks.append(task)
save_tasks(tasks)

That means:

Add task to the list.
Save the updated list to the file.

The important idea is:

The list is used while the program runs.
The file stores the data between program runs.

This is a very important programming pattern.

Memory in RAM.

Storage on disk.

Your program now uses both.

Very serious.

Very useful.

Writing vs Appending in the Task Manager

In save_tasks, we use "w":

with open(FILE_NAME, "w") as file:

Why not "a"?

Because we want to save the current full list.

If we used "a", tasks could be duplicated every time we save.

Example:

Buy milk
Study Python
Buy milk
Study Python

Not good.

So for saving the full list, use "w".

For adding one new line directly, use "a".

Simple rule:

Use "w" when saving the whole current state.
Use "a" when adding one new item to the end.

This is very important.

Tiny mode letter.

Big difference.

Common Mistake: Forgetting to Close the File

Manual way:

file = open("notes.txt", "w")
file.write("Hello")

This forgets:

file.close()

Better:

with open("notes.txt", "w") as file:
    file.write("Hello")

Use with open().

It closes the file automatically.

This is the style you should use most of the time.

Clean.

Safe.

Professional.

No forgotten open files wandering around like lost goats.

Common Mistake: Using w and Deleting Old Data

This can be dangerous:

with open("notes.txt", "w") as file:
    file.write("New note\n")

If notes.txt already had content, it is replaced.

Gone.

If you want to add to the file, use "a":

with open("notes.txt", "a") as file:
    file.write("New note\n")

Remember:

"w" overwrites.
"a" appends.

This is one of the most important file lessons.

Do not learn it the painful way.

The painful way is very effective.

But very painful.

Common Mistake: Forgetting Newline

Wrong:

with open("tasks.txt", "a") as file:
    file.write("Buy milk")
    file.write("Study Python")

File result:

Buy milkStudy Python

Correct:

with open("tasks.txt", "a") as file:
    file.write("Buy milk\n")
    file.write("Study Python\n")

File result:

Buy milk
Study Python

Use \n when each item should be on a new line.

Newlines are small.

But without them, text becomes pasta.

Not nice pasta.

Bug pasta.

Common Mistake: Reading Missing File

Wrong:

with open("tasks.txt", "r") as file:
    content = file.read()

This fails if tasks.txt does not exist.

Safer:

try:
    with open("tasks.txt", "r") as file:
        content = file.read()
except FileNotFoundError:
    content = ""

Now the program does not crash.

It uses empty content if the file is missing.

This is useful when the file will be created later.

Many beginner file programs fail on the first run.

Why?

Because the file does not exist yet.

Classic.

Very classic.

Common Mistake: Not Using strip()

When reading lines, you may get newline characters.

Example:

tasks = []

with open("tasks.txt", "r") as file:
    for line in file:
        tasks.append(line)

This may store:

["Buy milk\n", "Study Python\n"]

Better:

tasks.append(line.strip())

Now you get:

["Buy milk", "Study Python"]

Use .strip() when you want clean text.

Especially when reading line by line.

Your future output will look better.

And less haunted.

Common Mistake: File Path Confusion

This:

with open("tasks.txt", "r") as file:

looks for tasks.txt in the current working directory.

That means:

the folder where you run the Python command

If Python says the file is missing, maybe the file exists in another folder.

Example:

python scripts/task_manager.py

The current directory may not be the same as the script folder.

This can confuse beginners.

Very normal.

For now, keep your Python file and text file in the same folder.

Run the command from that folder.

Simple.

Less path drama.

Path drama is real.

And sometimes spicy.

Practice

Create a file:

practice_files.py

Write a program that:

Example solution:

name = input("Name: ")
city = input("City: ")

with open("profile.txt", "w") as file:
    file.write(name + "\n")
    file.write(city + "\n")

with open("profile.txt", "r") as file:
    lines = []

    for line in file:
        lines.append(line.strip())

print("----- Profile -----")
print(f"Name: {lines[0]}")
print(f"City: {lines[1]}")

Example:

Name: Anna
City: Rome

Output:

----- Profile -----
Name: Anna
City: Rome

This program writes data.

Then reads data.

Then uses the loaded data.

Very important pattern.

Save.

Load.

Use.

That is file handling in a nutshell.

A small nutshell.

With Python inside.

Mini Challenge

Create a file:

notes_app.py

Your program should:

Menu:

1. Add note
2. Show notes
q. Quit

Example solution:

FILE_NAME = "notes.txt"

def show_menu():
    print("----- Notes App -----")
    print("1. Add note")
    print("2. Show notes")
    print("q. Quit")

def add_note():
    note = input("Note: ")

    with open(FILE_NAME, "a") as file:
        file.write(note + "\n")

    print("Note saved.")

def show_notes():
    try:
        with open(FILE_NAME, "r") as file:
            print("Notes:")

            has_notes = False

            for line in file:
                print(f"- {line.strip()}")
                has_notes = True

            if not has_notes:
                print("No notes yet.")
    except FileNotFoundError:
        print("No notes yet.")

while True:
    show_menu()

    choice = input("Choose an option: ").lower()

    if choice == "1":
        add_note()
    elif choice == "2":
        show_notes()
    elif choice == "q":
        print("Goodbye.")
        break
    else:
        print("Unknown option.")

This is a real mini application.

It uses:

Very strong.

Very useful.

Very beginner project, but already real.

A small notes app that remembers notes.

Not bad at all.

Extra Challenge: Save Contacts

Create a file:

contacts_file.py

Build a program that:

Simple file format:

Anna,anna@example.com,123456
Marco,marco@example.com,987654

Example idea:

FILE_NAME = "contacts.txt"

def add_contact():
    name = input("Name: ")
    email = input("Email: ")
    phone = input("Phone: ")

    with open(FILE_NAME, "a") as file:
        file.write(f"{name},{email},{phone}\n")

    print("Contact saved.")

def show_contacts():
    try:
        with open(FILE_NAME, "r") as file:
            print("Contacts:")

            for line in file:
                parts = line.strip().split(",")

                if len(parts) == 3:
                    name = parts[0]
                    email = parts[1]
                    phone = parts[2]

                    print("-----")
                    print(f"Name: {name}")
                    print(f"Email: {email}")
                    print(f"Phone: {phone}")
    except FileNotFoundError:
        print("No contacts yet.")

while True:
    print("----- Contact Book -----")
    print("1. Add contact")
    print("2. Show contacts")
    print("q. Quit")

    choice = input("Choose an option: ").lower()

    if choice == "1":
        add_contact()
    elif choice == "2":
        show_contacts()
    elif choice == "q":
        print("Goodbye.")
        break
    else:
        print("Unknown option.")

This program saves structured data in a simple text format.

Important:

line.strip().split(",")

This takes a line like:

Anna,anna@example.com,123456

and turns it into:

["Anna", "anna@example.com", "123456"]

This is a very useful idea.

Later, you can learn better formats like JSON.

But for now, simple text files are enough.

One step at a time.

No need to summon the JSON dragon yet.

Beginner Checklist

When file code does not work, check:

Did I use with open()?
Did I choose the correct mode?
Did I accidentally use "w" instead of "a"?
Did I add "\n" for new lines?
Does the file exist before reading?
Should I handle FileNotFoundError?
Did I use strip() when reading lines?
Am I running Python from the correct folder?
Did I spell the file name correctly?
Did I save the file after writing the code?

File bugs are often simple.

But annoying.

Especially path problems.

If Python says:

FileNotFoundError

do not panic.

Check:

file name
folder
mode
current directory

Most of the time, the file is simply not where Python is looking.

Python is strict.

It does not search your whole computer emotionally.

It looks where you told it to look.

Very literal.

Very Python.

Summary

Today you learned:

This is a huge step.

Your programs can now remember things.

They can save data.

They can load data.

They can become more useful.

This is where small scripts begin to feel like real applications.

A program with memory is much more powerful than a program that forgets everything.

Even if the memory is just a tiny tasks.txt.

Small file.

Big upgrade.

Next Lesson

In the next lesson, we will learn basic error handling.

Your programs will learn how to survive problems more gracefully.

For example:

user enters text instead of a number
file does not exist
division by zero
wrong menu option
missing data

Instead of crashing dramatically, your program can respond calmly.

Very useful.

Very adult.

Very Python.

Because real users do strange things.

And sometimes the real user is you.

Especially at night.