March 10, 2026 · All About CS

File Handling in Python — Part 2: Appending, Deleting, the with Statement, and File Pointers

Complete your Python file handling knowledge — learn how to append data, safely delete files with the os module, use the with statement as a context manager, and understand how the file pointer drives every read and write operation.

pythonfile-handlingos-modulewith-statementfile-pointerbeginners
File Handling in PythonPart 2 of 2

File Handling in Python — Part 2: Appending, Deleting, the with Statement, and File Pointers

In Part 1, we covered the essentials: why files matter, how open() works, and how to create, write, and read files. This part completes the picture with the remaining operations — appending and deleting — plus two crucial concepts every Python developer needs: the with statement and the file pointer.

Key Takeaways

  • Append mode ("a") adds data at the end of a file without erasing what's already there — unlike write mode which overwrites.
  • Deleting files requires Python's built-in os module and its os.remove() function.
  • Always use os.path.exists() before reading or deleting a file to avoid a FileNotFoundError.
  • The with statement closes files automatically — even if an error occurs — and is the recommended approach in all professional Python code.
  • The file pointer is the internal cursor that determines where every read and write operation begins.

Appending to a File

So far, write mode ("w") has been our go-to for putting data into files. But every time you use it, the file's existing content is wiped out. Append mode ("a") solves this — it opens the file and positions the cursor at the very end, so new data is added after everything already there:

Python
my_file = open("stored_files/program_data.txt", "a")
my_file.write("\nHere goes some more data!")
my_file.close()

If program_data.txt already contained:

Hello There! Program data stored successfully! 1
Hello There! Program data stored successfully! 2

After running the above, it now contains:

Hello There! Program data stored successfully! 1
Hello There! Program data stored successfully! 2
Here goes some more data!

The original content is untouched. Every subsequent run of the same script adds another line below.

Write vs. Append — The Critical Difference

This distinction trips up almost every beginner at least once:

ModeWhat happens to existing contentFile pointer starts at
"w"Erased — completely overwrittenBeginning of file
"a"Preserved — new data added after itEnd of file

Run a program with "w" mode ten times: you'll only ever see the output from the most recent run. Run it with "a" mode ten times: every run's output is stacked in the file.

Python
# Demonstrating the difference:

# Write mode — only the last run survives
for i in range(3):
    with open("write_demo.txt", "w") as f:
        f.write(f"Run #{i + 1}\n")
# File contains only: "Run #3"

# Append mode — every run is preserved
for i in range(3):
    with open("append_demo.txt", "a") as f:
        f.write(f"Run #{i + 1}\n")
# File contains:
# Run #1
# Run #2
# Run #3

When to use append vs. write: Use "w" when you want to produce a fresh file each run — reports, exports, rendered templates. Use "a" when you want to accumulate data over multiple runs — logs, audit trails, data collection scripts.


Deleting Files with the os Module

Python's built-in open() handles creating, reading, writing, and appending — but not deletion. For that, you need the os module, which is part of Python's standard library (no installation required):

Python
import os

os.remove("stored_files/program_data.txt")

After this runs, the file is permanently removed from disk.

Defensive Deletion — Avoiding FileNotFoundError

If the file doesn't exist when os.remove() is called, Python raises a FileNotFoundError. The fix is to check first with os.path.exists():

Python
import os

file_path = "stored_files/program_data.txt"

if os.path.exists(file_path):
    os.remove(file_path)
    print(f"Deleted: {file_path}")
else:
    print(f"File not found — nothing to delete.")

Now the program behaves gracefully whether or not the file exists.

Apply the same check before reading. Attempting to open a non-existent file in "r" mode raises the same FileNotFoundError. Use os.path.exists() before any read operation on a file whose existence you can't guarantee:

Python
import os

if os.path.exists("stored_files/program_data.txt"):
    with open("stored_files/program_data.txt", "r") as f:
        print(f.read())
else:
    print("File does not exist yet.")

The with Statement — The Professional Way to Handle Files

Every example in Part 1 required a manual .close() call. Forget it once — because of an early return, an exception, or simply oversight — and the file stays locked until your program exits. The with statement eliminates this problem entirely:

Python
with open("stored_files/program_data.txt", "w") as my_file:
    my_file.write("Hello There! Program data stored successfully!\n")
    my_file.write("Hello There! Program data stored successfully!\n")
    my_file.write("Hello There! Program data stored successfully!\n")

# File is automatically closed here — no .close() needed

The with block is a context manager: it guarantees the file is closed the moment the indented block ends, even if an error is raised inside it. This is the standard pattern in all professional Python code.

🖼️ Visual Suggestion: Side-by-side comparison of the manual open/close pattern vs. the with statement, highlighting where .close() is called in each.

All Three Operations Using with

Writing:

Python
with open("data.txt", "w") as f:
    f.write("Fresh content.\n")

Reading:

Python
with open("data.txt", "r") as f:
    content = f.read()
    print(content)

Appending:

Python
with open("data.txt", "a") as f:
    f.write("Appended safely.\n")

Try converting the read and append examples from Part 1 to use the with statement — it's a good exercise to solidify the pattern.


Understanding the File Pointer

Every time you open a file, Python creates an internal cursor called the file pointer (also known as the file handle). It determines the exact position in the file where the next read or write operation will begin.

🖼️ Visual Suggestion: Diagram of a text file with a visible arrow representing the file pointer, showing its starting position in "r", "w", and "a" modes respectively.

How the Pointer Behaves by Mode

ModePointer starts atEffect
"r"Beginning of fileReads forward from the start
"w"Beginning of fileErases file, writes from the start
"a"End of fileWrites after all existing content
"r+"Beginning of fileReads and writes from the start
"a+"End of fileCan read, but pointer starts at end — reads return empty unless repositioned

This is the deeper reason why write mode overwrites and append mode preserves: they start the pointer in different places. Write mode clears the file and places the pointer at position 0. Append mode places the pointer at the last byte of existing content and adds from there.

How the Pointer Moves During Read Operations

When you call .read(), the pointer moves all the way to the end of the file in one step. When you call .readline(), it moves to the start of the next line:

Python
with open("data.txt", "r") as f:
    print(f.readline())   # → Line 1  (pointer now at start of Line 2)
    print(f.readline())   # → Line 2  (pointer now at start of Line 3)
    print(f.tell())       # → current byte position of the pointer

.tell() returns the current byte offset of the pointer — useful for debugging.

Manually Repositioning the Pointer with .seek()

You can move the pointer to any position using .seek():

Python
with open("data.txt", "r") as f:
    content = f.read()    # reads entire file, pointer is now at the end
    f.seek(0)             # reset pointer to the beginning
    first_line = f.readline()  # reads Line 1 again
    print(first_line)

This also explains why "a+" mode can't read existing content without a manual seek: the pointer starts at the end, so a .read() call immediately finds nothing. You'd need f.seek(0) first to reposition it to the start.

Python
with open("data.txt", "a+") as f:
    f.seek(0)             # move pointer to the beginning
    print(f.read())       # now this works
    f.write("\nNew line") # appending still works — write always goes to the end in 'a' mode

Putting It All Together — A Practical Example

Here's a script that demonstrates the complete file handling lifecycle: create, write, read, append, check existence, and delete:

Python
import os

FILE_PATH = "stored_files/session_log.txt"

# 1. Create and write
with open(FILE_PATH, "w") as f:
    f.write("Session started.\n")
    f.write("User logged in.\n")

# 2. Read back what was written
with open(FILE_PATH, "r") as f:
    print("--- Initial content ---")
    print(f.read())

# 3. Append a new entry
with open(FILE_PATH, "a") as f:
    f.write("User performed an action.\n")

# 4. Read again to confirm append
with open(FILE_PATH, "r") as f:
    print("--- After append ---")
    print(f.read())

# 5. Safely delete the file
if os.path.exists(FILE_PATH):
    os.remove(FILE_PATH)
    print("Log file deleted.")

Output:

--- Initial content ---
Session started.
User logged in.

--- After append ---
Session started.
User logged in.
User performed an action.

Log file deleted.

File Handling Cheat Sheet

Python
import os

# Write (creates file, overwrites existing content)
with open("file.txt", "w") as f:
    f.write("content\n")

# Append (adds to end, preserves existing content)
with open("file.txt", "a") as f:
    f.write("more content\n")

# Read entire file
with open("file.txt", "r") as f:
    data = f.read()

# Read line by line
with open("file.txt", "r") as f:
    for line in f:
        print(line.strip())

# Read all lines into a list
with open("file.txt", "r") as f:
    lines = f.readlines()

# Check file exists before reading or deleting
if os.path.exists("file.txt"):
    with open("file.txt", "r") as f:
        print(f.read())

# Delete a file safely
if os.path.exists("file.txt"):
    os.remove("file.txt")

# Get current pointer position
with open("file.txt", "r") as f:
    f.read()
    print(f.tell())   # byte position at end of file

# Reset pointer to beginning
with open("file.txt", "r") as f:
    f.read()
    f.seek(0)
    print(f.readline())  # back to line 1

What's Next?

Now that you can persist data reliably and handle files safely, the next step is learning what to do when things go wrong. The next tutorial covers error handling in Python — using try, except, and finally to write programs that fail gracefully instead of crashing unexpectedly.

Happy coding. 🐍

File Handling in PythonPart 2 of 2