Writing Output Files: Save Your Results to Disk
Learn to write data to files using write mode ('w'), and complete the read → clean → write cycle by saving a formatted sales report.
You've been printing to the screen for 26 days. Today Python finally makes something permanent.
Think about what you've built: You read a CSV file. You cleaned the records. You formatted the output. Then you printed it to the terminal. And the moment you closed the terminal, it was gone.
Wait, I did all that work and there's nothing left?
Gone. Vanished. But today? We save it.
The Problem: Everything Disappears
Your script works like this:
records = parse_csv('sales.csv')
clean_records = [clean_record(r) for r in records]
for r in clean_records:
formatted = f"{r['name']} ({r['region']}): ${r['amount']:.2f} [{r['status']}]"
print(formatted) # Print it to the screen
The output looks great on screen. But then? Nothing. No trace. If you want that report tomorrow, you have to run the script again.
Maya(thinking): So I need to save it to a file instead of printing?
Close! You do both. Print for immediate feedback. Write for permanence. Today, we focus on the write part.
Opening a File in Write Mode
Remember from Day 24? You opened a file for reading:
with open('sales.csv') as f: # Default mode: 'r' for read
lines = f.readlines()
Now we open it for writing:
with open('report.txt', 'w') as f: # Mode: 'w' for write
f.write('Hello, file!\n')
Mode 'w' does something important: it erases the file if it already exists. Then it writes your content.
Wait, it deletes the file first?
Yes. 'w' mode clears the old content. This is intentional—you're replacing the file entirely. If you want to add to an existing file without erasing it, use 'a' mode (append).
# 'w' mode: erase everything, write fresh
with open('report.txt', 'w') as f:
f.write('New content') # Old content is gone
# 'a' mode: add to the end
with open('report.txt', 'a') as f:
f.write('\nMore content') # Old content stays
For your sales report, 'w' is what you want. Fresh report every time.
Writing Strings: f.write() vs. print()
You know print(). It adds a newline automatically:
print('Hello') # Adds '\n' at the end
print('World')
# Output:
# Hello
# World
But f.write() is different. It writes exactly what you give it. No newline unless you add \n yourself:
with open('report.txt', 'w') as f:
f.write('Hello') # Just 'Hello', no newline
f.write('World') # Appended to same line: 'HelloWorld'
You have to be explicit:
with open('report.txt', 'w') as f:
f.write('Hello\n') # Add the newline yourself
f.write('World\n')
# Output:
# Hello
# World
Maya(excited): So I control exactly what gets written?
Exactly. That control is powerful. No hidden newlines. No invisible characters. Just what you put in.
Building a Sales Report
Now let's combine it all. You have clean records from Day 26. You format them with the format_sale function from Week 1. Then you write each to a file.
def write_report(records, filepath):
with open(filepath, 'w') as f:
# Write the header
f.write('Sales Report\n')
f.write('-' * 40 + '\n') # Separator line
# Write each record
for r in records:
# Reuse the format from Week 1
line = f"{r['name']} ({r['region']}): ${r['amount']:.2f} [{r['status']}]\n"
f.write(line)
records = [
{'name': 'Alice Chen', 'amount': 1250.00, 'region': 'WEST', 'status': 'confirmed'},
{'name': 'Bob Kumar', 'amount': 340.50, 'region': 'EAST', 'status': 'pending'},
]
write_report(records, 'report.txt')
print('Report written!') # Feedback to the user
Let's trace through:
- Open
report.txtin write mode—creates the file if it doesn't exist, erases it if it does - Write the header line (remember to add
\n) - Write a separator (just decoration)
- Loop through records
- Format each one using the Week 1 pattern (name, region, amount, status)
- Write each formatted line to the file (remember
\nagain) - Exit the
withblock—file automatically closes - Print a confirmation to the terminal
Now you have report.txt on disk. Run the script again tomorrow? The old report gets replaced.
And then I can open it in a text editor and it's there?
It's there. Real file. Real data. Persistent.
The Complete Journey
This is the first time you've completed the full cycle:
Day 24: READ → CSV file on disk → Python (as strings)
Day 25: PARSE → Strings → Dicts (cleaned, structured)
Day 26: CLEAN → Dicts → More refined dicts (proper types, normalized)
Day 27: WRITE → Dicts → Formatted text → File on disk
Data in. Data out. Permanent.
Maya(thinking): Wait, tomorrow we put it all together?
** Tomorrow. Day 28. One function that does all four. The pipeline complete.
Your Challenge
Write write_report(records, filepath) that:
- Opens the file in write mode (
'w') - Writes a header line:
'Sales Report\n' - For each record, writes one line using the format:
f"{r['name']} ({r['region']}): ${r['amount']:.2f} [{r['status']}]\n" - Closes the file (handled by
with)
The test data is provided. Run the function. Then check that report.txt exists on disk and contains the header and both records properly formatted.
Stretch: Add a separator line (dashes) between the header and the records, and a footer line at the end showing the total amount across all records.
Next time: The final assembly. One function that orchestrates everything: read the file, parse it, clean it, write the report. All the pieces. One pipeline.
Practice your skills
Sign up to write and run code in this lesson.
Writing Output Files: Save Your Results to Disk
Learn to write data to files using write mode ('w'), and complete the read → clean → write cycle by saving a formatted sales report.
You've been printing to the screen for 26 days. Today Python finally makes something permanent.
Think about what you've built: You read a CSV file. You cleaned the records. You formatted the output. Then you printed it to the terminal. And the moment you closed the terminal, it was gone.
Wait, I did all that work and there's nothing left?
Gone. Vanished. But today? We save it.
The Problem: Everything Disappears
Your script works like this:
records = parse_csv('sales.csv')
clean_records = [clean_record(r) for r in records]
for r in clean_records:
formatted = f"{r['name']} ({r['region']}): ${r['amount']:.2f} [{r['status']}]"
print(formatted) # Print it to the screen
The output looks great on screen. But then? Nothing. No trace. If you want that report tomorrow, you have to run the script again.
Maya(thinking): So I need to save it to a file instead of printing?
Close! You do both. Print for immediate feedback. Write for permanence. Today, we focus on the write part.
Opening a File in Write Mode
Remember from Day 24? You opened a file for reading:
with open('sales.csv') as f: # Default mode: 'r' for read
lines = f.readlines()
Now we open it for writing:
with open('report.txt', 'w') as f: # Mode: 'w' for write
f.write('Hello, file!\n')
Mode 'w' does something important: it erases the file if it already exists. Then it writes your content.
Wait, it deletes the file first?
Yes. 'w' mode clears the old content. This is intentional—you're replacing the file entirely. If you want to add to an existing file without erasing it, use 'a' mode (append).
# 'w' mode: erase everything, write fresh
with open('report.txt', 'w') as f:
f.write('New content') # Old content is gone
# 'a' mode: add to the end
with open('report.txt', 'a') as f:
f.write('\nMore content') # Old content stays
For your sales report, 'w' is what you want. Fresh report every time.
Writing Strings: f.write() vs. print()
You know print(). It adds a newline automatically:
print('Hello') # Adds '\n' at the end
print('World')
# Output:
# Hello
# World
But f.write() is different. It writes exactly what you give it. No newline unless you add \n yourself:
with open('report.txt', 'w') as f:
f.write('Hello') # Just 'Hello', no newline
f.write('World') # Appended to same line: 'HelloWorld'
You have to be explicit:
with open('report.txt', 'w') as f:
f.write('Hello\n') # Add the newline yourself
f.write('World\n')
# Output:
# Hello
# World
Maya(excited): So I control exactly what gets written?
Exactly. That control is powerful. No hidden newlines. No invisible characters. Just what you put in.
Building a Sales Report
Now let's combine it all. You have clean records from Day 26. You format them with the format_sale function from Week 1. Then you write each to a file.
def write_report(records, filepath):
with open(filepath, 'w') as f:
# Write the header
f.write('Sales Report\n')
f.write('-' * 40 + '\n') # Separator line
# Write each record
for r in records:
# Reuse the format from Week 1
line = f"{r['name']} ({r['region']}): ${r['amount']:.2f} [{r['status']}]\n"
f.write(line)
records = [
{'name': 'Alice Chen', 'amount': 1250.00, 'region': 'WEST', 'status': 'confirmed'},
{'name': 'Bob Kumar', 'amount': 340.50, 'region': 'EAST', 'status': 'pending'},
]
write_report(records, 'report.txt')
print('Report written!') # Feedback to the user
Let's trace through:
- Open
report.txtin write mode—creates the file if it doesn't exist, erases it if it does - Write the header line (remember to add
\n) - Write a separator (just decoration)
- Loop through records
- Format each one using the Week 1 pattern (name, region, amount, status)
- Write each formatted line to the file (remember
\nagain) - Exit the
withblock—file automatically closes - Print a confirmation to the terminal
Now you have report.txt on disk. Run the script again tomorrow? The old report gets replaced.
And then I can open it in a text editor and it's there?
It's there. Real file. Real data. Persistent.
The Complete Journey
This is the first time you've completed the full cycle:
Day 24: READ → CSV file on disk → Python (as strings)
Day 25: PARSE → Strings → Dicts (cleaned, structured)
Day 26: CLEAN → Dicts → More refined dicts (proper types, normalized)
Day 27: WRITE → Dicts → Formatted text → File on disk
Data in. Data out. Permanent.
Maya(thinking): Wait, tomorrow we put it all together?
** Tomorrow. Day 28. One function that does all four. The pipeline complete.
Your Challenge
Write write_report(records, filepath) that:
- Opens the file in write mode (
'w') - Writes a header line:
'Sales Report\n' - For each record, writes one line using the format:
f"{r['name']} ({r['region']}): ${r['amount']:.2f} [{r['status']}]\n" - Closes the file (handled by
with)
The test data is provided. Run the function. Then check that report.txt exists on disk and contains the header and both records properly formatted.
Stretch: Add a separator line (dashes) between the header and the records, and a footer line at the end showing the total amount across all records.
Next time: The final assembly. One function that orchestrates everything: read the file, parse it, clean it, write the report. All the pieces. One pipeline.