You need Python installed, and to know how to run a
.py file. If python3 --version in a
terminal prints a version number, you are ready. If it does not — or
if you have never opened a terminal — do
Getting Started with
Python first; it installs Python and runs your first program, and
takes about fifteen minutes. Nothing else is needed: no account, no
payment, no extra software.
Opening and Closing Files
Python uses the built-in open() function to work with files. The most
important rule: always close files when you're done with them.
# Basic pattern (not recommended)
file = open("example.txt", "r")
content = file.read()
file.close()
# Better: use 'with' statement (auto-closes)
with open("example.txt", "r") as file:
content = file.read()
# File is automatically closed here
with statement
The with statement guarantees the file is closed even if an error
occurs. Forgetting to close files can lead to data loss and resource leaks.
File Modes
The second argument to open() specifies how the file should be opened:
# Read modes
"r" # Read (default) — file must exist
"r+" # Read and write — file must exist
# Write modes
"w" # Write — creates file or OVERWRITES existing
"a" # Append — creates file or adds to end
"x" # Exclusive create — fails if file exists
# Binary modes (add 'b')
"rb" # Read binary (images, PDFs, etc.)
"wb" # Write binary
"w" mode erases everything!
Opening a file with "w" immediately deletes all existing content.
Use "a" to add to a file without losing data.
Reading Files
Read Entire File
with open("notes.txt", "r") as file:
content = file.read()
print(content)
Read Line by Line
# Read all lines into a list
with open("notes.txt", "r") as file:
lines = file.readlines()
for line in lines:
print(line.strip()) # strip() removes trailing newline
# Memory-efficient: iterate directly
with open("notes.txt", "r") as file:
for line in file:
print(line.strip())
file.read() loads the entire file into memory. For large files,
iterate line by line with for line in file: — this reads only one
line at a time.
Read Specific Amount
with open("notes.txt", "r") as file:
first_100 = file.read(100) # Read first 100 characters
next_line = file.readline() # Read next line from current position
Writing Files
Write Text
# Create or overwrite a file
with open("output.txt", "w") as file:
file.write("First line\n")
file.write("Second line\n")
# Write multiple lines at once
lines = ["Line 1\n", "Line 2\n", "Line 3\n"]
with open("output.txt", "w") as file:
file.writelines(lines)
Append to a File
# Add to existing file without erasing
with open("log.txt", "a") as file:
file.write("New log entry\n")
Write with print()
with open("output.txt", "w") as file:
print("Hello, file!", file=file)
print(f"The answer is {42}", file=file)
Working with File Paths
import os
from pathlib import Path
# os.path (traditional)
print(os.path.exists("myfile.txt")) # True/False
print(os.path.isfile("myfile.txt")) # True if it's a file
print(os.path.isdir("myfolder")) # True if it's a directory
print(os.path.join("folder", "file.txt")) # folder/file.txt
# pathlib (modern, recommended)
p = Path("folder") / "subfolder" / "file.txt"
print(p) # folder/subfolder/file.txt
print(p.exists()) # True/False
print(p.suffix) # .txt
print(p.stem) # file
print(p.parent) # folder/subfolder
Practical Examples
Log Writer
from datetime import datetime
def log(message, filename="app.log"):
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
with open(filename, "a") as file:
file.write(f"[{timestamp}] {message}\n")
log("Application started")
log("User logged in")
log("Error: connection timeout")
Simple CSV Reader
# Reading CSV manually
with open("data.csv", "r") as file:
header = file.readline().strip().split(",")
print(f"Columns: {header}")
for line in file:
values = line.strip().split(",")
row = dict(zip(header, values))
print(row)
# Better: use the csv module
import csv
with open("data.csv", "r") as file:
reader = csv.DictReader(file)
for row in reader:
print(row["name"], row["age"])
Word Counter
def count_words(filename):
with open(filename, "r") as file:
text = file.read()
words = text.split()
lines = text.count("\n") + 1
chars = len(text)
print(f"File: {filename}")
print(f" Lines: {lines}")
print(f" Words: {len(words)}")
print(f" Characters: {chars}")
count_words("example.txt")
Checking If a File Exists
from pathlib import Path
filepath = Path("config.txt")
if filepath.exists():
content = filepath.read_text()
print("File found!")
else:
print("File not found, creating it...")
filepath.write_text("default config")
Now Do It Yourself: Five Steps
Reading and writing files is how a program remembers anything after it stops. You will create a file, read it back two different ways, add to it, and meet the one mistake that silently destroys data. Every output below is exactly what Python printed.
Go: open a terminal, run cd ~, and create a file called notes.py in your editor.
Do: type these two lines and run python3 notes.py.
with open("notes.txt", "w") as f:
f.write("first line\nsecond line\n")
You should see: no output at all — but a new file notes.txt now sits beside your script. Check with cat notes.txt (or type notes.txt on Windows): two lines. The \n is a newline character; without it everything would run together on one line.
If not: if you cannot find the file, it was created wherever the terminal was, not where the script lives. Run pwd to see where that is. The with is what guarantees the file is closed and the text actually flushed to disk — open a file without it and your text can still be sitting in memory when the program ends.
Go: same file, replace the contents.
Do: type this one line and run it.
print(open("notes.txt").read(), end="")
You should see: first line and second line. read() hands back the entire file as one string, newlines included — which is why end="" is there, to stop print adding a third blank line of its own.
If not: FileNotFoundError: [Errno 2] No such file or directory: 'notes.txt' means step 1 has not run, or ran somewhere else. The message names the file it looked for; the folder it looked in is wherever your terminal is.
Go: same file, replace the contents.
Do: type these three lines and run it.
with open("notes.txt") as f:
for line in f:
print("[" + line.strip() + "]")
You should see: [first line] and [second line]. The brackets are there to prove the point: each line arrives with its newline still attached, and strip() is what removes it. Looping over the file this way reads one line at a time, so it works on a file far too big to fit in memory.
If not: if you see a blank line between each result, you left out strip() — the line's own newline plus print's newline makes two. That is the single most common surprise when reading files.
Go: same file, replace the contents.
Do: type these three lines and run it. Note the "a".
with open("notes.txt", "a") as f:
f.write("third line\n")
print(open("notes.txt").read(), end="")
You should see: all three lines. "a" means append — write to the end and keep what is already there.
If not: if only third line appears, you used "w" instead of "a". Read the next step before you do that to anything you care about.
Go: same file, replace the contents.
Do: type these two lines and run it.
open("notes.txt", "w").close()
print("after opening with w, length is", len(open("notes.txt").read()))
You should see: after opening with w, length is 0. Nothing was written — merely opening with "w" emptied the file, instantly and with no warning or confirmation.
If not: if the length is not zero, you used "a" or "r". This is the most destructive everyday mistake in file handling, and it is why you read first and write second, never the other way round. Before running any script that writes to a file that matters, copy it: cp notes.txt notes.txt.bak.
Without scrolling up: you want to add a line to an existing log file. Which mode, and
what happens if you pick the other one? Answer: "a". Picking
"w" empties the file the instant it opens, before you write a single
character.
Now do it without the page: write a script that reads
notes.txt, counts the lines, and appends a final line saying how many there
were. Read fully first, then append — steps 3 and 4 joined up.
Summary
- Always use
with open(...) as file:to automatically close files - File modes:
"r"(read),"w"(write/overwrite),"a"(append),"x"(create new) read()loads entire file; iterate withfor line in file:for large fileswrite()andwritelines()write to files; remember\nfor newlines- Use
pathlib.Pathfor modern, cross-platform file path handling - The
csvmodule handles CSV files properly (handles quoting, escaping)
Your programs can now persist data to disk and read external data. Next up: error handling — making your programs robust when things go wrong.