Reading Files
File I/O (Input/Output) is how your program interacts with files on disk — reading configuration, processing data, saving results. Python makes file handling simple with the built-in open() function and the with statement for automatic cleanup.
The most important rule: always use the with statement. It guarantees the file is properly closed even if an error occurs. Without it, you risk file handle leaks that can cause data corruption or "too many open files" errors.
Reading Entire Files
# Read entire file as a single string
with open("data.txt", "r") as f:
content = f.read()
print(content)
# Read all lines into a list
with open("data.txt", "r") as f:
lines = f.readlines() # Each line includes the \n
print(f"Got {len(lines)} lines")
# Read line by line (memory efficient for large files)
with open("data.txt", "r") as f:
for line in f:
print(line.strip()) # strip() removes the trailing \n
File Modes
"r" = read (default), "w" = write (overwrites!), "a" = append, "x" = create (fails if exists), "rb"/"wb" = binary mode. Always specify the mode explicitly for clarity.
Writing Files
Writing works the same way — open with "w" (overwrite) or "a" (append). Be careful with "w" mode: it erases all existing content when the file is opened.
# Write mode — creates or OVERWRITES the file
with open("output.txt", "w") as f:
f.write("Line 1\n")
f.write("Line 2\n")
f.write(f"Generated on 2024-01-15\n")
# Append mode — adds to the end
with open("output.txt", "a") as f:
f.write("Appended line\n")
# Write multiple lines at once
lines = ["Hello\n", "World\n", "Python\n"]
with open("output.txt", "w") as f:
f.writelines(lines)
with open(...) as f: for file operations. It guarantees cleanup. Never use f = open(...) without a corresponding f.close() in a finally block.Working with JSON
JSON (JavaScript Object Notation) is the most common data exchange format on the web. Python's built-in json module converts between Python dictionaries/lists and JSON strings seamlessly:
import json
# Python dict to JSON file
data = {
"name": "Alice",
"age": 30,
"scores": [95, 87, 92],
"active": True,
"address": None
}
# Write to file (indent for readability)
with open("data.json", "w") as f:
json.dump(data, f, indent=2)
# Read from file
with open("data.json", "r") as f:
loaded = json.load(f)
print(loaded["name"])
print(loaded["scores"])
# String conversion (no file involved)
json_string = json.dumps(data, indent=2)
print(json_string)
parsed = json.loads('{"key": "value"}')
print(parsed)
Alice
[95, 87, 92]
{
"name": "Alice",
"age": 30,
"scores": [95, 87, 92],
"active": true,
"address": null
}
{'key': 'value'}| Function | Direction | Target |
|---|---|---|
json.dump(obj, file) | Python → File | Write to file |
json.load(file) | File → Python | Read from file |
json.dumps(obj) | Python → String | Convert to string |
json.loads(string) | String → Python | Parse from string |
Working with CSV
CSV (Comma-Separated Values) files are everywhere in data processing. Python's csv module handles quoting, escaping, and different delimiters automatically:
import csv
# Write CSV
with open("people.csv", "w", newline="") as f:
writer = csv.DictWriter(f, fieldnames=["name", "age", "city"])
writer.writeheader()
writer.writerow({"name": "Alice", "age": 30, "city": "NYC"})
writer.writerow({"name": "Bob", "age": 25, "city": "LA"})
# Read CSV
with open("people.csv", "r") as f:
reader = csv.DictReader(f)
for row in reader:
print(f"{row['name']} ({row['age']}) - {row['city']}")
Alice (30) - NYC Bob (25) - LA
pathlib — Modern Path Handling
The pathlib module (Python 3.4+) provides an object-oriented way to work with file paths. It's cleaner and more portable than string concatenation with os.path:
from pathlib import Path
# Build paths with / operator (works on all OS)
data_dir = Path("data")
file_path = data_dir / "reports" / "2024" / "summary.txt"
print(file_path) # data/reports/2024/summary.txt
# Path properties
print(file_path.suffix) # .txt
print(file_path.stem) # summary
print(file_path.name) # summary.txt
print(file_path.parent) # data/reports/2024
# Quick read/write (no need for open())
Path("hello.txt").write_text("Hello, World!")
content = Path("hello.txt").read_text()
print(content)
# List files
for f in Path(".").glob("*.py"):
print(f"Python file: {f}")
# Check existence
if Path("config.json").exists():
print("Config found!")
data/reports/2024/summary.txt .txt summary summary.txt data/reports/2024 Hello, World!
⚠️ Common Mistake: Not Using Context Managers
Wrong:
f = open("file.txt", "r")
data = f.read()
# If an error occurs here, file handle leaks!
f.close()
Why: If an exception occurs between open() and close(), the file is never closed. This leaks file handles and can corrupt data.
Instead:
with open("file.txt", "r") as f:
data = f.read()
# File is automatically closed, even if an error occurs
🔍 Deep Dive: Binary vs Text Mode
By default, open() uses text mode ("r"/"w"), which handles encoding (UTF-8 by default) and converts line endings on Windows (\\r\\n to \\n). For images, PDFs, zip files, or any non-text data, use binary mode ("rb"/"wb"). Binary mode reads/writes raw bytes without any conversion.