Python

Stop Using os.path: Why pathlib Will Change How You Handle Files

Learn why pathlib is the modern Python way to handle file paths: more readable, cross-platform, and less error-prone than os.path. Includes practical examples and a real downloads organizer script.

July 2026 6 min read 11 views 0 hearts

Stop Using os.path: Why pathlib Will Change How You Handle Files in Python

I remember the exact moment I discovered pathlib. I was debugging a script that used os.path.join() with a mix of forward and backward slashes, wondering why my colleague's Windows machine kept crashing while my Linux system worked fine. Three hours later, I rewrote everything using pathlib, and I haven't touched os.path since.

Let me show you why.

What Makes pathlib Different?

The traditional way of handling file paths in Python looks like this:

import os

path = os.path.join("data", "images", "photo.jpg")
if os.path.exists(path):
    with open(path, "r") as f:
        content = f.read()

It works, but it's messy. You're juggling strings, remembering which function does what, and hoping your slashes are correct.

Enter pathlib. Instead of working with strings, you work with objects:

from pathlib import Path

path = Path("data/images/photo.jpg")  # Slashes work on any OS
if path.exists():
    content = path.read_text()  # One method call instead of three

See the difference? The Path object handles operating system differences automatically. No more worrying about Windows vs Linux slashes.

Getting Started: Creating Path Objects

Creating a path is straightforward:

from pathlib import Path

# Current directory
current = Path(".")

# Home directory
home = Path.home()

# Absolute path
absolute = Path("/usr/local/bin")

# Relative path
relative = Path("docs/manual.pdf")

You can also combine paths cleanly:

base = Path("project")
config = base / "config" / "settings.json"
# Result: project/config/settings.json (on Linux/Mac)
# Result: project\config\settings.json (on Windows)

The / operator works as path joining. No more os.path.join() confusion.

Common File Operations Made Simple

Here's where pathlib really shines. Let me show you some everyday operations:

Reading and Writing Files

path = Path("notes.txt")

# Read entire file
content = path.read_text()

# Write to file
path.write_text("Hello, world!")

# Append to file
with path.open("a") as f:
    f.write("More content")

No more with open() boilerplate for simple operations. Though for binary files, use read_bytes() and write_bytes():

image = Path("photo.png")
data = image.read_bytes()

Checking File Properties

path = Path("document.pdf")

print(path.exists())        # True or False
print(path.is_file())       # Is it a file?
print(path.is_dir())        # Is it a directory?
print(path.stat().st_size)  # File size in bytes
print(path.suffix)          # .pdf
print(path.stem)            # document (without extension)
print(path.name)            # document.pdf

Working with Directories

docs = Path("documents")

# List all files in directory
for file in docs.iterdir():
    print(file.name)

# List only PDF files
for pdf in docs.glob("*.pdf"):
    print(pdf)

# Recursive search
for all_files in docs.rglob("*"):
    print(all_files)

rglob("*") is a personal favorite. It recursively finds everything inside a directory tree.

Creating and Deleting Files

path = Path("new_folder")

# Create directory (and parents if needed)
path.mkdir(exist_ok=True, parents=True)

# Create empty file
file = path / "example.txt"
file.touch()

# Delete file
file.unlink()

# Delete empty directory
path.rmdir()

# Delete directory and all contents
import shutil
shutil.rmtree(path)

Always use exist_ok=True with mkdir() unless you specifically want an error. Your future self will thank you.

Real Example: Organizing a Downloads Folder

Let me give you something practical. Here's a script that organizes your downloads by file type:

from pathlib import Path
import shutil

def organize_downloads(downloads_path="~/Downloads"):
    downloads = Path(downloads_path).expanduser()

    if not downloads.exists():
        print("Downloads folder not found")
        return

    file_types = {
        "Images": [".jpg", ".jpeg", ".png", ".gif"],
        "Documents": [".pdf", ".docx", ".txt", ".xlsx"],
        "Archives": [".zip", ".tar", ".gz"],
        "Videos": [".mp4", ".avi", ".mkv"]
    }

    for file in downloads.iterdir():
        if file.is_file():
            moved = False
            for folder, extensions in file_types.items():
                if file.suffix.lower() in extensions:
                    target = downloads / folder
                    target.mkdir(exist_ok=True)
                    shutil.move(str(file), str(target / file.name))
                    print(f"Moved {file.name} to {folder}")
                    moved = True
                    break

            if not moved:
                other = downloads / "Other"
                other.mkdir(exist_ok=True)
                shutil.move(str(file), str(other / file.name))
                print(f"Moved {file.name} to Other")

if __name__ == "__main__":
    organize_downloads()

Run this once a week, and your Downloads folder stays clean.

Platform Independence: The Hidden Superpower

Here's something that bit me hard in production. Consider this code:

# Bad: breaks on Windows
path = "data/images/" + filename

# Bad: might break with different OS
path = os.path.join("data", "images", filename)

# Good: works everywhere
path = Path("data/images") / filename

The last version handles everything. Forward slashes, backward slashes, mix of both — pathlib normalizes it all. When I deployed a Flask app that used pathlib, I never got another "file not found" error from Windows users.

Path Manipulation Without Fear

Need to change a file extension? Easy:

report = Path("report.docx")
new_report = report.with_suffix(".pdf")
# Result: report.pdf

Need to go up a directory?

current = Path("/home/user/project/src/file.py")
parent = current.parent  # /home/user/project/src
grandparent = current.parent.parent  # /home/user/project

Need absolute paths?

relative = Path("docs/manual.pdf")
absolute = relative.absolute()  # /home/user/docs/manual.pdf

When Not to Use pathlib

I'll be honest — pathlib isn't perfect for everything. Here's when I still use os.path:

  1. Extreme performance scenarios: os.path can be slightly faster for millions of operations
  2. Legacy code maintenance: Don't refactor working code just for fun
  3. Very simple scripts: If you're just checking if one file exists, os.path.exists() is fine

But for 95% of file operations, pathlib is the better choice.

Final Thoughts

When I switched to pathlib at PythonSkillset, our error rates for file-related bugs dropped significantly. The code became more readable, more reliable, and easier to maintain.

Start small. Next time you write a script that touches files, import Path instead of os.path. After a few scripts, you'll wonder why you ever used strings for file paths.

The only cost? Adding one import line. The benefit? File operations that actually make sense.

Comments

Questions, corrections, and tips stay visible for everyone reading this page.

0 in thread

Join the discussion

Shown next to your comment.

Up to 4,000 characters

No comments yet

Be the first to leave a note — it helps the next reader.