Modern File Paths with pathlib

Reviewed & published by Brayan K

For years Python developers juggled file paths as fragile strings with os.path . pathlib replaced all of that with clean Path objects that know how to join, search, read, and write. It's the modern standard, and once you use it you won't go back.

Learn Modern File Paths with pathlib in our free Python course — a beginner-friendly interactive lesson with runnable examples, a practice exercise and a…

Part of the free Python course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

You'll build paths with the / operator, inspect filenames, find files with glob patterns, and read and write files in one line.

What You'll Learn in This Lesson

1 Paths as Objects, Not Strings

The old way joined strings by hand — brittle and platform-dependent. The new way uses the / operator, which reads exactly like a real path:

2 Inspecting a Path

A Path object exposes every part of a filename as a simple attribute — no more string slicing:

Attribute

For "data/report.csv"

🔍 Checking What Exists

Before reading or writing, you often need to know what's actually there. These return booleans:

These checks prevent the most common file errors. Checking exists() before reading turns a crash into a clean message.

3 Reading & Writing in One Line

For small text and data files, read_text and write_text handle opening and closing the file for you — no with open(...) boilerplate:

🗂️ Finding Files with glob & rglob

Need every CSV in a folder? Every Python file in an entire project tree? glob and rglob make it trivial:

4 Creating and Removing Folders

🧪 Worked Example — The Whole Lifecycle in One Run

The sections above showed each method on its own. This program strings them together the way a real script does: build a path, create the folders, write files, ask questions about them, read one back, search for them, and tidy up. It really does touch the disk, so run it and then run it a second time — thanks to exist_ok=True nothing breaks.

That blank line in the middle of the output is not a mistake: the CSV text ends with a newline and print adds one of its own. It is the commonest source of "why is there a gap in my output?" — print(text, end="") removes it.

🎯 Your Turn — Make a Folder, Fill It, Find It

Everything is written except the four pieces this lesson is about: the two mkdir arguments, the one-line write method, and the glob pattern.

A FileNotFoundError on the mkdir line means the parents argument is still blank — Python will not invent the missing practice folder for you unless you ask it to.

Real-World Example: An Auto-Filing Organiser

A genuinely useful script: scan a messy "Downloads" folder and sort every file into a subfolder by its extension. This is the kind of thing people actually run weekly:

Every line uses pathlib: iterdir to list, suffix to classify, / to build the destination, mkdir to create folders, rename to move. No string juggling anywhere.

🎯 Mini-Challenge: Sort a Messy Folder

You read the auto-filing organiser above. Now write one. The setup lines build the mess for you; the rest is an outline with no logic in it. This is a script people genuinely run on their own Downloads folder, so it is worth being able to write from memory.

Why sorted(messy.iterdir()) rather than messy.iterdir() ? Two reasons: the output order becomes predictable, and sorted reads the whole listing up front, so the folders you create inside the loop cannot confuse the loop that is creating them.

🧩 Reorder Challenge

These lines should make a folder and write a file inside it, but they're scrambled. Find the order:

Import, build the Path, create the folder (with parents so data/ is made too), then write the file. You must create the folder before writing into it.

🧠 Quick Recall — Predict the Output

a/b/c.txt (on Windows: a\b\c.txt ) — the / operator joins path parts using the OS separator.

.gz — .suffix is only the LAST extension. To get both you'd use .suffixes , which returns ['.tar', '.gz'] .

today — .stem is the filename without its extension and without the folder. ( .name would be today.md .)

❓ Frequently Asked Questions

Files and folders bend to your will now!

You can build paths with the / operator, inspect every part of a filename, check existence, read and write in one line, find files with glob, and create folders safely. pathlib is the modern way and you've mastered the essentials.

🚀 Up next: Checkpoint — A Real-World Script — combine everything you've learned into one practical program.

Practice quiz

What does Path("a") / "b" / "c.txt" produce on Linux?

  • a/b/c.txt
  • a\b\c.txt
  • abc.txt
  • a.b.c.txt

Answer: a/b/c.txt. The / operator joins path parts with the OS separator — forward slashes on Linux/Mac.

What is Path("archive.tar.gz").suffix?

  • .tar.gz
  • .tar
  • .gz
  • tar.gz

Answer: .gz. .suffix returns only the LAST extension. Use .suffixes to get ['.tar', '.gz'].

What is Path("/data/notes/today.md").stem?

  • today.md
  • today
  • notes
  • .md

Answer: today. .stem is the filename without its final extension and without the folder.

What is Path("/home/user/reports/q3.final.csv").name?

  • q3
  • q3.final
  • q3.final.csv
  • reports

Answer: q3.final.csv. .name is the full final component, including all extensions.

Which attribute gives the containing folder of a path?

  • .parent
  • .stem
  • .suffix
  • .name

Answer: .parent. For 'data/report.csv', .parent is 'data' — the folder holding the file.

What does Path("/data/report.csv").with_suffix(".json") return?

  • /data/report.json.csv
  • /data/report.json
  • /data/.json
  • report.json

Answer: /data/report.json. with_suffix swaps the last extension, giving /data/report.json.

Which method searches recursively through ALL subfolders for a pattern?

  • glob
  • iterdir
  • rglob
  • walk

Answer: rglob. rglob('*.py') recurses into every subfolder; glob only looks in one folder.

Which call creates a folder chain safely without erroring if it already exists?

  • p.mkdir()
  • p.mkdir(parents=True, exist_ok=True)
  • p.makedirs()
  • p.create(recursive=True)

Answer: p.mkdir(parents=True, exist_ok=True). parents=True makes missing parents (like mkdir -p); exist_ok=True avoids FileExistsError.

Which one-line call returns a file's whole contents as a string?

  • p.read()
  • p.read_text()
  • p.text()
  • p.open_text()

Answer: p.read_text(). read_text() opens, reads, and closes the file, returning a str — no with-open needed.

What does p.write_text("hi") do if the file already exists?

  • Appends to the end
  • Raises FileExistsError
  • Overwrites the entire file
  • Does nothing

Answer: Overwrites the entire file. write_text overwrites the whole file every time. Use p.open('a') to append instead.

Continue this course