Day 27 · ~15m

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.

teacher (neutral)

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.

student (struggling)

Wait, I did all that work and there's nothing left?

teacher (amused)

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?

teacher (encouraging)

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.

student (confused)

Wait, it deletes the file first?

teacher (focused)

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?

teacher (proud)

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:

  1. Open report.txt in write mode—creates the file if it doesn't exist, erases it if it does
  2. Write the header line (remember to add \n)
  3. Write a separator (just decoration)
  4. Loop through records
  5. Format each one using the Week 1 pattern (name, region, amount, status)
  6. Write each formatted line to the file (remember \n again)
  7. Exit the with block—file automatically closes
  8. Print a confirmation to the terminal

Now you have report.txt on disk. Run the script again tomorrow? The old report gets replaced.

student (amused)

And then I can open it in a text editor and it's there?

teacher (excited)

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?

teacher (proud)

** Tomorrow. Day 28. One function that does all four. The pipeline complete.

Your Challenge

Write write_report(records, filepath) that:

  1. Opens the file in write mode ('w')
  2. Writes a header line: 'Sales Report\n'
  3. For each record, writes one line using the format: f"{r['name']} ({r['region']}): ${r['amount']:.2f} [{r['status']}]\n"
  4. 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.