Stop Writing File Paths Like It's 1999: Python's pathlib Is What You Actually Need

Stop Writing File Paths Like It’s 1999 — Python’s pathlib Is What You Actually Need

Beyond the Basics · Python Toolkit Series · #2


Here’s a line of Python we usually write:

import os

path = os.path.join(os.path.dirname(__file__), "data", "report.csv")

You’re importing os, calling os.path, calling os.path.join, calling os.path.dirname, passing __file__… to access “the data/report.csv file.”

With Path library there’s a better way to do it.

from pathlib import Path

path = Path(__file__).parent / "data" / "report.csv"

That / isn’t division — it’s pathlib’s way of joining paths.


What’s Wrong with os.path?

os.path has two problems:

**1. Your path is a string. You have to call a function every time you want to do anything with it — get its name, its extension, its parent folder. The path itself knows nothing.

2. The API is scattered. Need to read a file? That’s open(). Check if it exists? os.path.exists(). Get the filename? os.path.basename(). Create a folder? os.makedirs(). You’re jumping between os, os.path, and built-ins constantly.

pathlib fixes both. A Path object knows things about itself, and you call methods directly on it.


The Core Idea: Paths Are Objects, Not Strings

from pathlib import Path

p = Path("/home/user/projects/data/report.csv")

print(p.name)       # report.csv
print(p.stem)       # report  (name without extension)
print(p.suffix)     # .csv
print(p.parent)     # /home/user/projects/data
print(p.parts)      # ('/', 'home', 'user', 'projects', 'data', 'report.csv')

It gives easier way to understand the path.

Compare that to os.path:

import os

p = "/home/user/projects/data/report.csv"

print(os.path.basename(p))                # report.csv
print(os.path.splitext(os.path.basename(p))[0])  # report — look at this mess
print(os.path.splitext(p)[1])             # .csv
print(os.path.dirname(p))                 # /home/user/projects/data

It takes much more effort just for basic details.


4 Places Where pathlib Wins Clearly

1. Building Paths

os.path:

config_file = os.path.join(os.path.expanduser("~"), ".config", "myapp", "settings.json")

pathlib:

config_file = Path.home() / ".config" / "myapp" / "settings.json"

pathlib uses / to define the path.


2. Reading and Writing Files

With os.path, you still need open(). With pathlib, the path itself can read and write:

from pathlib import Path

p = Path("notes.txt")

# Write
p.write_text("Hello, pathlib!")

# Read
content = p.read_text()
print(content)  # Hello, pathlib!

No with open(...) as f, no f.read(). Just .write_text() and .read_text(). For quick file operations, It’s the hardest way.


3. Checking and Creating

os.path:

if not os.path.exists("logs"):
    os.makedirs("logs")

pathlib:

Path("logs").mkdir(exist_ok=True)

exist_ok=True means “create it if it doesn’t exist, do nothing if it already does.” One line. No condition needed.


4. Finding Files (Glob)

This is where pathlib does better. Want every .csv file in a folder, including subfolders?

os.path way — you’d need os.walk(), write a loop, filter extensions manually.

pathlib way:

from pathlib import Path

data_dir = Path("data")

# All CSVs in this folder
for file in data_dir.glob("*.csv"):
    print(file)

# All CSVs in this folder AND all subfolders
for file in data_dir.rglob("*.csv"):
    print(file)

glob() and rglob() return path objects, not strings. So you can immediately call .name, .stem, .read_text().


Works on Windows and Mac/Linux — Automatically

This is the real advantage.

On Windows, paths use backslashes: C:\Users\user\data
On Mac/Linux, they use forward slashes: /home/user/data

When you use os.path.join(), you have to be careful. When you use pathlib, it handles this for you automatically. The same code works everywhere.


One Pattern You’ll Use Every Day

If you write Python scripts and work with files relative to where your script lives, this is the pattern:

from pathlib import Path

# This is the folder your script is in — not wherever you run it from
BASE_DIR = Path(__file__).parent

data_file   = BASE_DIR / "data" / "input.csv"
output_file = BASE_DIR / "output" / "results.csv"
config_file = BASE_DIR / "config.json"

With os.path, this is os.path.join(os.path.dirname(os.path.abspath(__file__)), ...) — and you’d have to repeat it for every file. With pathlib, you define BASE_DIR once and build everything from it cleanly.


The Quick Reference

from pathlib import Path

p = Path("data/report.csv")

# Info about the path
p.name          # report.csv
p.stem          # report
p.suffix        # .csv
p.parent        # data/
p.exists()      # True or False

# Building paths
new_path = p.parent / "archive" / p.name

# Reading and writing
p.write_text("content")
text = p.read_text()
p.write_bytes(b"bytes")
data = p.read_bytes()

# Creating folders
Path("logs").mkdir(parents=True, exist_ok=True)

# Finding files
list(Path(".").glob("*.py"))         # current folder
list(Path(".").rglob("*.py"))        # all subfolders too

# Useful extras
Path.home()     # /home/username
Path.cwd()      # current working directory
p.resolve()     # absolute path, symlinks resolved

Should You Go Back and Rewrite Old Code?

Not necessarily. If your os.path code works, leave it. But for anything new you write — especially scripts that touch files — reach for pathlib first. The code will be shorter, cleaner, and easier to read later.

And if you’re working on a team? pathlib code is far easier to review. Path("data") / "report.csv" explains itself. os.path.join("data", "report.csv") makes you double-check what it’s doing.


The Bottom Line

os.path asks you to call a function for everything. pathlib lets the path carry its own meaning. Once you switch, string-based paths start looking like unnecessary friction.

It’s built into Python. No install needed.

from pathlib import Path

That’s the whole setup.


This is post #2 in the Beyond the Basics series — exploring Python libraries that show up in real work.

Post #1 covered rich — the library that turns your terminal output from noise into signal. Check it out if you missed it.

*Enjoyed this? The next post covers Typer — popular Python library designed to build clean, powerful command-line interface (CLI) applications using modern Python type hints