pathlib and File System Operations

Difficulty: Beginner

Question

Why is pathlib preferred over os.path for file system operations in Python?

Answer

pathlib (Python 3.4+) provides an object-oriented path manipulation API that: - Works on all platforms (Windows uses backslash, Unix uses forward slash - pathlib handles both) - Supports method chaining with / operator - Returns Path objects with useful methods - Integrates with built-in open(), shutil, etc.

vs os.path: - os.path is string-based (fragile concatenation) - Less readable: os.path.join(os.path.dirname(file), 'data', 'file.csv') - vs pathlib: Path(__file__).parent / 'data' / 'file.csv'

Code examples

pathlib Basics

from pathlib import Path

# Current file location
p = Path(__file__).resolve()
print(p.name)       # script.py
print(p.stem)       # script
print(p.suffix)     # .py
print(p.parent)     # /path/to/

# Path construction with / operator
data_dir = Path('data')
config = data_dir / 'config' / 'settings.json'
print(config)       # data/config/settings.json

# Existence and type checks
path = Path('.')
print(path.exists())   # True
print(path.is_dir())   # True
print(path.is_file())  # False

# Reading and writing
file = Path('output.txt')
file.write_text('Hello, world!', encoding='utf-8')
content = file.read_text(encoding='utf-8')
print(content)  # Hello, world!

# Creating directories
Path('data/nested').mkdir(parents=True, exist_ok=True)

# Listing files
for f in Path('.').glob('*.py'):
    print(f.name)

Path objects support / for joining, method calls for properties, and built-in read/write methods. mkdir(parents=True, exist_ok=True) is the safe way to create nested directories.

Advanced pathlib Operations

from pathlib import Path
import shutil

# Glob patterns
base = Path('project')
base.mkdir(exist_ok=True)

# Recursive glob
python_files = list(base.rglob('*.py'))
test_files = list(base.glob('/test_*.py'))

# Rename and move
original = Path('report.txt')
new_name = original.with_name('report_2024.txt')
new_ext = original.with_suffix('.md')
# original.rename(new_name)

# Copy with shutil (pathlib handles paths, shutil does operations)
src = Path('source.txt')
dst = Path('backup/source.txt')
dst.parent.mkdir(parents=True, exist_ok=True)
# shutil.copy2(src, dst)

# Relative paths
target = Path('/home/user/project/data/file.csv')
base_path = Path('/home/user/project')
print(target.relative_to(base_path))  # data/file.csv

# File info
if Path('example.txt').exists():
    stat = Path('example.txt').stat()
    print(f'Size: {stat.st_size} bytes')
    import datetime
    print(f'Modified: {datetime.datetime.fromtimestamp(stat.st_mtime)}')

rglob('*.py') recursively finds all .py files. relative_to() computes the path relative to a base. with_suffix() and with_name() cleanly modify paths.

Key points

Concepts covered

pathlib, Path, os.path, glob, file operations