Section 10 introduced the basics of pathlib. This lesson covers more of what Path can do — useful patterns for real projects.
Recap — the essentials
from pathlib import Path
p: Path = Path("data/users.csv")
p.exists()
p.is_file()
p.is_dir()
p.read_text(encoding="utf-8")
p.write_text("Hello", encoding="utf-8")
p / "subfolder" / "file.txt" # joining
p.parent # containing folder
p.name # 'users.csv'
p.stem # 'users'
p.suffix # '.csv'
Globbing
Path.glob finds files matching a pattern in a single folder. Path.rglob searches recursively.
# all CSVs in data/
for f in Path("data").glob("*.csv"):
print(f)
# all Python files anywhere below the current dir
for f in Path(".").rglob("*.py"):
print(f)
Patterns:
*— any name within one path part**— any depth (only valid withrglob)?— any single character[abc]— character class
# any 4-character name
Path(".").glob("????.txt")
# files starting with "data_"
Path(".").glob("data_*.json")
Iterating a directory
iterdir() yields every immediate child of a directory:
for child in Path("data").iterdir():
print(child)
Combine with .is_file() or .suffix to filter:
for child in Path("data").iterdir():
if child.is_file() and child.suffix == ".csv":
print(child)
Renaming and moving
old: Path = Path("draft.txt")
new: Path = Path("final.txt")
old.rename(new)
rename works across folders too:
Path("draft.txt").rename(Path("archive") / "draft.txt")
The target folder must exist.
Replacing — replace
replace is like rename, but overwrites the destination if it exists:
Path("temp.txt").replace(Path("data.txt")) # data.txt is overwritten
rename raises on Windows when the destination exists; replace handles both platforms consistently. Prefer replace when overwriting is intentional.
Creating folders
Path("logs/2026/may").mkdir(parents=True, exist_ok=True)
parents=True— create any missing parentsexist_ok=True— succeed if the folder already exists
Without exist_ok=True, mkdir raises if the folder exists.
Deleting
Path("temp.txt").unlink(missing_ok=True) # delete file
Path("empty_dir").rmdir() # delete empty dir
For non-empty directories, use shutil:
import shutil
shutil.rmtree("some_folder")
File metadata — stat
p: Path = Path("data.csv")
info = p.stat()
print(info.st_size) # size in bytes
print(info.st_mtime) # modified time (seconds since epoch)
print(info.st_atime) # last access time
print(info.st_ctime) # creation/change time
For a human-readable modified time:
from datetime import datetime
modified = datetime.fromtimestamp(p.stat().st_mtime)
print(modified)
Path comparisons
Two paths are equal if their string representations match:
Path("a/b") == Path("a/b") # True
Path("a/b") == Path("a/b/c").parent # True
For “is this path inside that path?”, use is_relative_to (Python 3.9+):
Path("/home/me/code/file.py").is_relative_to("/home/me") # True
Cross-platform paths
pathlib chooses the right path style based on the OS — PosixPath on macOS and Linux, WindowsPath on Windows. You write the same Python code; pathlib handles the difference.
If you need to construct Windows-style paths on a non-Windows machine (rare), use PureWindowsPath:
from pathlib import PureWindowsPath
win_path = PureWindowsPath("C:/Users/You/file.txt")
print(win_path) # 'C:\\Users\\You\\file.txt'
PurePath types don’t touch the actual filesystem — they’re useful for path manipulation when the path is for another OS.
A practical example — clean up old log files
Find every .log file older than 7 days and delete it:
from datetime import datetime, timedelta
from pathlib import Path
cutoff = datetime.now() - timedelta(days=7)
for log in Path("logs").rglob("*.log"):
modified = datetime.fromtimestamp(log.stat().st_mtime)
if modified < cutoff:
print(f"Removing {log}")
log.unlink()
Three modules together — pathlib, datetime, and the standard for loop — make this a small but real script.
What’s next
Final lesson of this section — os and sys, for talking to the operating system.