pathlib is the modern way to handle file paths in Python. It's cleaner and more intuitive than os.path.
Creating Paths
from pathlib import Path
# Current directory
cwd = Path.cwd()
# Home directory
home = Path.home()
# From string
path = Path("/usr/local/bin")
# Joining paths (use /)
config = Path.home() / ".config" / "myapp"Path Components
path = Path("/home/user/documents/report.pdf")
path.name # "report.pdf"
path.stem # "report"
path.suffix # ".pdf"
path.parent # Path("/home/user/documents")
path.parents # All ancestors
path.parts # ('/', 'home', 'user', 'documents', 'report.pdf')
path.anchor # "/"Checking Paths
path = Path("myfile.txt")
path.exists() # Does it exist?
path.is_file() # Is it a file?
path.is_dir() # Is it a directory?
path.is_symlink() # Is it a symbolic link?
path.is_absolute() # Is it an absolute path?Creating Files and Directories
# Create directory (and parents)
Path("data/processed").mkdir(parents=True, exist_ok=True)
# Create empty file (or update timestamp)
Path("output.txt").touch()Reading and Writing
path = Path("data.txt")
# Write text
path.write_text("Hello, World!")
# Read text
content = path.read_text()
# Write bytes
path.write_bytes(b"\x00\x01\x02")
# Read bytes
data = path.read_bytes()
# With encoding
path.write_text("Héllo", encoding="utf-8")
content = path.read_text(encoding="utf-8")Listing Directories
dir_path = Path("src")
# All items
for item in dir_path.iterdir():
print(item)
# Pattern matching
for py_file in dir_path.glob("*.py"):
print(py_file)
# Recursive
for py_file in dir_path.rglob("*.py"):
print(py_file)
# As list
python_files = list(dir_path.glob("**/*.py"))Path Operations
path = Path("src/module/file.py")
# Resolve to absolute
abs_path = path.resolve()
# Get relative path
rel_path = path.relative_to("src") # Path("module/file.py")
# Change extension
new_path = path.with_suffix(".txt") # src/module/file.txt
# Change name
new_path = path.with_name("other.py") # src/module/other.py
# Change stem (keep extension)
new_path = path.with_stem("renamed") # src/module/renamed.pyFile Operations
from pathlib import Path
import shutil
src = Path("source.txt")
dst = Path("dest.txt")
# Rename/move
src.rename(dst)
# Copy (use shutil)
shutil.copy(src, dst)
shutil.copytree(Path("dir1"), Path("dir2"))
# Delete file
path.unlink()
# Delete empty directory
path.rmdir()
# Delete directory tree (use shutil)
shutil.rmtree(Path("dir_to_delete"))File Metadata
path = Path("myfile.txt")
stat = path.stat()
stat.st_size # Size in bytes
stat.st_mtime # Modification time
stat.st_ctime # Creation time (or metadata change on Unix)
# Convenient access
from datetime import datetime
modified = datetime.fromtimestamp(path.stat().st_mtime)Working with Config Files
from pathlib import Path
import json
config_path = Path.home() / ".config" / "myapp" / "config.json"
# Ensure directory exists
config_path.parent.mkdir(parents=True, exist_ok=True)
# Write config
config_path.write_text(json.dumps({"theme": "dark"}))
# Read config
config = json.loads(config_path.read_text())Temporary Files
from pathlib import Path
import tempfile
# Temp directory
with tempfile.TemporaryDirectory() as tmpdir:
tmp_path = Path(tmpdir)
(tmp_path / "data.txt").write_text("temporary")
# Directory deleted after block
# Temp file path
tmp_file = Path(tempfile.mktemp(suffix=".txt"))Common Patterns
Safe file writing
def safe_write(path: Path, content: str):
"""Write atomically to avoid corruption."""
tmp = path.with_suffix(".tmp")
tmp.write_text(content)
tmp.rename(path)Find project root
def find_project_root(marker: str = "pyproject.toml") -> Path:
"""Find parent directory containing marker file."""
current = Path.cwd()
for parent in [current, *current.parents]:
if (parent / marker).exists():
return parent
raise FileNotFoundError(f"No {marker} found")Process all files
def process_directory(root: Path, pattern: str = "*.txt"):
for file_path in root.rglob(pattern):
content = file_path.read_text()
# Process content
file_path.write_text(content.upper())os.path vs pathlib
# Old way (os.path)
import os
path = os.path.join(os.path.expanduser("~"), ".config", "app")
if os.path.exists(path) and os.path.isdir(path):
files = os.listdir(path)
# New way (pathlib)
from pathlib import Path
path = Path.home() / ".config" / "app"
if path.is_dir():
files = list(path.iterdir())Quick Reference
from pathlib import Path
# Create
Path("dir").mkdir(parents=True, exist_ok=True)
Path("file.txt").touch()
# Read/Write
content = Path("f.txt").read_text()
Path("f.txt").write_text("data")
# Navigate
Path.cwd() # Current directory
Path.home() # Home directory
path / "child" # Join paths
path.parent # Parent directory
path.resolve() # Absolute path
# Inspect
path.exists()
path.is_file()
path.is_dir()
path.suffix # ".txt"
path.stem # "file"
# List
path.iterdir() # Direct children
path.glob("*.py") # Pattern match
path.rglob("*.py") # Recursive matchpathlib makes file operations readable and cross-platform. Use it instead of os.path.
React to this post: