File operations are fundamental. Here's how to do them properly.

Reading Files

# Basic reading
with open("file.txt") as f:
    content = f.read()
 
# Read lines
with open("file.txt") as f:
    lines = f.readlines()  # List of lines
 
# Iterate lines (memory efficient)
with open("file.txt") as f:
    for line in f:
        process(line.strip())

Writing Files

# Write (overwrites)
with open("file.txt", "w") as f:
    f.write("Hello, World!")
 
# Append
with open("file.txt", "a") as f:
    f.write("\nNew line")
 
# Write lines
with open("file.txt", "w") as f:
    f.writelines(["line1\n", "line2\n"])

File Modes

ModeDescription
rRead (default)
wWrite (overwrites)
aAppend
xCreate (fails if exists)
bBinary mode
+Read and write
# Binary
with open("image.png", "rb") as f:
    data = f.read()
 
# Read and write
with open("file.txt", "r+") as f:
    content = f.read()
    f.write("appended")

pathlib (Modern Approach)

from pathlib import Path
 
# Create path
path = Path("folder/file.txt")
 
# Read/write
content = path.read_text()
path.write_text("Hello!")
 
# Binary
data = path.read_bytes()
path.write_bytes(data)

Path Operations

from pathlib import Path
 
path = Path("/home/user/documents/file.txt")
 
# Components
path.name        # "file.txt"
path.stem        # "file"
path.suffix      # ".txt"
path.parent      # Path("/home/user/documents")
path.parts       # ('/', 'home', 'user', 'documents', 'file.txt')
 
# Build paths
new_path = path.parent / "other.txt"

Checking Files

from pathlib import Path
 
path = Path("file.txt")
 
path.exists()      # True/False
path.is_file()     # Is regular file
path.is_dir()      # Is directory
path.is_absolute() # Is absolute path

Directory Operations

from pathlib import Path
 
folder = Path("my_folder")
 
# Create directory
folder.mkdir()
folder.mkdir(parents=True, exist_ok=True)
 
# List contents
for item in folder.iterdir():
    print(item)
 
# Find files
for py_file in folder.glob("*.py"):
    print(py_file)
 
# Recursive glob
for py_file in folder.rglob("*.py"):
    print(py_file)

Working with CSV

import csv
 
# Read CSV
with open("data.csv") as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)
 
# Read as dicts
with open("data.csv") as f:
    reader = csv.DictReader(f)
    for row in reader:
        print(row["name"], row["email"])
 
# Write CSV
with open("output.csv", "w", newline="") as f:
    writer = csv.writer(f)
    writer.writerow(["name", "email"])
    writer.writerow(["Owen", "owen@example.com"])

Working with JSON

import json
from pathlib import Path
 
# Read JSON
data = json.loads(Path("data.json").read_text())
 
# Write JSON
Path("output.json").write_text(
    json.dumps(data, indent=2)
)
 
# Or with file handle
with open("data.json") as f:
    data = json.load(f)
 
with open("output.json", "w") as f:
    json.dump(data, f, indent=2)

Encoding

# Specify encoding (important!)
with open("file.txt", encoding="utf-8") as f:
    content = f.read()
 
# pathlib
path.read_text(encoding="utf-8")
path.write_text(content, encoding="utf-8")

Always specify encoding to avoid platform-dependent issues.

Temporary Files

import tempfile
 
# Temporary file (auto-deleted)
with tempfile.NamedTemporaryFile(mode="w", delete=False) as f:
    f.write("temporary content")
    temp_path = f.name
 
# Temporary directory
with tempfile.TemporaryDirectory() as tmpdir:
    # Use tmpdir
    pass  # Deleted when exiting

Copying and Moving

import shutil
from pathlib import Path
 
# Copy file
shutil.copy("source.txt", "dest.txt")
shutil.copy2("source.txt", "dest.txt")  # Preserves metadata
 
# Copy directory
shutil.copytree("source_dir", "dest_dir")
 
# Move
shutil.move("source.txt", "dest.txt")
 
# Delete
Path("file.txt").unlink()  # Delete file
shutil.rmtree("directory")  # Delete directory

Safe File Operations

from pathlib import Path
import tempfile
import shutil
 
def safe_write(path: Path, content: str) -> None:
    """Write atomically to avoid corruption."""
    path = Path(path)
    
    # Write to temp file first
    with tempfile.NamedTemporaryFile(
        mode="w",
        dir=path.parent,
        delete=False
    ) as f:
        f.write(content)
        temp_path = Path(f.name)
    
    # Atomic rename
    temp_path.replace(path)

Error Handling

from pathlib import Path
 
path = Path("file.txt")
 
try:
    content = path.read_text()
except FileNotFoundError:
    print("File not found")
except PermissionError:
    print("Permission denied")
except IsADirectoryError:
    print("Expected file, got directory")

My Patterns

from pathlib import Path
import json
 
def load_json(path: str | Path) -> dict:
    """Load JSON file with error handling."""
    path = Path(path)
    if not path.exists():
        return {}
    return json.loads(path.read_text(encoding="utf-8"))
 
def save_json(path: str | Path, data: dict) -> None:
    """Save JSON file with pretty printing."""
    path = Path(path)
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(
        json.dumps(data, indent=2, ensure_ascii=False),
        encoding="utf-8"
    )

Use pathlib. Specify encoding. Handle errors. Use context managers.

React to this post: