Python pathlib — Modern File Paths
pathlibpathlib provides an object-oriented way to work with filesystem paths. Instead of juggling strings and os.path.joinos.path.join, you use a PathPath object with clean attributes and methods that work the same on Windows, macOS, and Linux.
quickstart.py
from pathlib import Path
p = Path("data") / "reports" / "2025.csv" # join with the / operator
print(p) # data/reports/2025.csv (uses the OS separator)
print(p.name) # 2025.csv
print(p.suffix) # .csv
print(p.parent) # data/reportsquickstart.py
from pathlib import Path
p = Path("data") / "reports" / "2025.csv" # join with the / operator
print(p) # data/reports/2025.csv (uses the OS separator)
print(p.name) # 2025.csv
print(p.suffix) # .csv
print(p.parent) # data/reportsWhy pathlib over os.path?
os.pathos.path (old) | pathlibpathlib (modern) |
|---|---|
os.path.join(a, b)os.path.join(a, b) | Path(a) / bPath(a) / b |
os.path.basename(p)os.path.basename(p) | p.namep.name |
os.path.splitext(p)[1]os.path.splitext(p)[1] | p.suffixp.suffix |
os.path.exists(p)os.path.exists(p) | p.exists()p.exists() |
| Strings everywhere. | One tidy object. |
Building paths
Use the // operator to join parts — it’s readable and cross-platform.
building.py
from pathlib import Path
base = Path("/home/user")
config = base / "app" / "config.json"
print(config) # /home/user/app/config.json
# Current working directory and home directory
print(Path.cwd()) # where the script runs
print(Path.home()) # the user's home folderbuilding.py
from pathlib import Path
base = Path("/home/user")
config = base / "app" / "config.json"
print(config) # /home/user/app/config.json
# Current working directory and home directory
print(Path.cwd()) # where the script runs
print(Path.home()) # the user's home folderInspecting a path
| Attribute / method | Returns |
|---|---|
p.namep.name | Final component (2025.csv2025.csv). |
p.stemp.stem | Name without suffix (20252025). |
p.suffixp.suffix | The extension (.csv.csv). |
p.parentp.parent | The containing directory. |
p.partsp.parts | A tuple of all components. |
p.with_suffix(".txt")p.with_suffix(".txt") | A new path with a changed extension. |
p.with_name("new.csv")p.with_name("new.csv") | A new path with a changed final name. |
inspecting.py
from pathlib import Path
p = Path("reports/q1/sales.csv")
print(p.name) # sales.csv
print(p.stem) # sales
print(p.suffix) # .csv
print(p.parent) # reports/q1
print(p.parts) # ('reports', 'q1', 'sales.csv')
print(p.with_suffix(".json")) # reports/q1/sales.jsoninspecting.py
from pathlib import Path
p = Path("reports/q1/sales.csv")
print(p.name) # sales.csv
print(p.stem) # sales
print(p.suffix) # .csv
print(p.parent) # reports/q1
print(p.parts) # ('reports', 'q1', 'sales.csv')
print(p.with_suffix(".json")) # reports/q1/sales.jsonChecking existence and type
checks.py
from pathlib import Path
p = Path("notes.txt")
print(p.exists()) # True if the path exists
print(p.is_file()) # True if it's a file
print(p.is_dir()) # True if it's a directorychecks.py
from pathlib import Path
p = Path("notes.txt")
print(p.exists()) # True if the path exists
print(p.is_file()) # True if it's a file
print(p.is_dir()) # True if it's a directoryReading and writing files
pathlibpathlib can read and write whole files in one line — no open()open() boilerplate for simple cases.
read_write.py
from pathlib import Path
p = Path("greeting.txt")
p.write_text("Hello, file!", encoding="utf-8") # write (and create)
print(p.read_text(encoding="utf-8")) # Hello, file!
# Binary variants
p.write_bytes(b"\x00\x01")
print(p.read_bytes()) # b'\x00\x01'read_write.py
from pathlib import Path
p = Path("greeting.txt")
p.write_text("Hello, file!", encoding="utf-8") # write (and create)
print(p.read_text(encoding="utf-8")) # Hello, file!
# Binary variants
p.write_bytes(b"\x00\x01")
print(p.read_bytes()) # b'\x00\x01'Listing and globbing directories
listing.py
from pathlib import Path
folder = Path(".")
# Everything directly inside
for item in folder.iterdir():
print(item)
# Only .py files in this folder
for py in folder.glob("*.py"):
print(py)
# Recursively, every .py file under here
for py in folder.rglob("*.py"):
print(py)listing.py
from pathlib import Path
folder = Path(".")
# Everything directly inside
for item in folder.iterdir():
print(item)
# Only .py files in this folder
for py in folder.glob("*.py"):
print(py)
# Recursively, every .py file under here
for py in folder.rglob("*.py"):
print(py)| Method | Finds |
|---|---|
p.iterdir()p.iterdir() | Every item directly inside pp. |
p.glob(pattern)p.glob(pattern) | Items matching a pattern in pp. |
p.rglob(pattern)p.rglob(pattern) | Matches recursively in all subfolders. |
Creating and removing
create_remove.py
from pathlib import Path
d = Path("output/logs")
d.mkdir(parents=True, exist_ok=True) # create folders; don't error if present
f = d / "run.log"
f.touch() # create an empty file
f.unlink(missing_ok=True) # delete a file (no error if missing)create_remove.py
from pathlib import Path
d = Path("output/logs")
d.mkdir(parents=True, exist_ok=True) # create folders; don't error if present
f = d / "run.log"
f.touch() # create an empty file
f.unlink(missing_ok=True) # delete a file (no error if missing)Common pitfalls
Path("a") / "b"Path("a") / "b"only works with a Path on the left —"a" / Path("b")"a" / Path("b")fails; start from aPathPath.p.suffixp.suffixincludes the dot —.csv.csv, notcsvcsv.mkdirmkdirerrors if the parent is missing — passparents=Trueparents=True.read_textread_textraises if the file doesn’t exist — checkp.exists()p.exists()first or catch the error.- Convert to
str(p)str(p)only when a library insists on a string.
Practice Exercises
Exercise 1 – Extract the file extension
Exercise 2 – Join paths with the / operator
Exercise 3 – Name without the extension
Summary
pathlib.Pathpathlib.Pathis the modern, object-oriented replacement foros.pathos.path.- Join paths with
//; inspect them withnamename,stemstem,suffixsuffix,parentparent,partsparts. - Read/write files directly with
read_textread_text/write_textwrite_text(and_bytes_bytesvariants). - List and search folders with
iterdiriterdir,globglob, andrglobrglob. - Create and remove with
mkdir(parents=True, exist_ok=True)mkdir(parents=True, exist_ok=True),touchtouch, andunlinkunlink.
If this helped you, consider buying me a coffee ☕
Buy me a coffeeWas this page helpful?
Let us know how we did
