Skip to content

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/reports
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/reports

Why 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 folder
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 folder

Inspecting a path

Attribute / methodReturns
p.namep.nameFinal component (2025.csv2025.csv).
p.stemp.stemName without suffix (20252025).
p.suffixp.suffixThe extension (.csv.csv).
p.parentp.parentThe containing directory.
p.partsp.partsA 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.json
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.json

Checking 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 directory
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 directory

Reading 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)
MethodFinds
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 a PathPath.
  • p.suffixp.suffix includes the dot.csv.csv, not csvcsv.
  • mkdirmkdir errors if the parent is missing — pass parents=Trueparents=True.
  • read_textread_text raises if the file doesn’t exist — check p.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.Path is the modern, object-oriented replacement for os.pathos.path.
  • Join paths with //; inspect them with namename, stemstem, suffixsuffix, parentparent, partsparts.
  • Read/write files directly with read_textread_text/write_textwrite_text (and _bytes_bytes variants).
  • List and search folders with iterdiriterdir, globglob, and rglobrglob.
  • Create and remove with mkdir(parents=True, exist_ok=True)mkdir(parents=True, exist_ok=True), touchtouch, and unlinkunlink.

If this helped you, consider buying me a coffee ☕

Buy me a coffee

Was this page helpful?

Let us know how we did