skip to content

pathlib β€” Object-Oriented File Paths

Work with filesystem paths using Python's built-in pathlib module. Covers Path creation, navigation, reading/writing files, glob patterns, and stat.

3 min read 10 snippets yesterday quick read

pathlib β€” Object-Oriented File Paths#

What it is#

pathlib is part of the Python standard library (no install needed). It represents filesystem paths as Path objects instead of plain strings, giving you methods for reading, writing, navigating, and querying files β€” all in a cross-platform way that handles Windows backslashes automatically.

It replaces os.path, os.getcwd(), open() boilerplate, and glob.glob() for most filesystem tasks.

Quick example#

from pathlib import Path

p = Path("documents/readme.txt")
print(p.name)       # filename with extension
print(p.stem)       # filename without extension
print(p.suffix)     # extension with dot
print(p.parent)     # containing directory
print(p.parts)      # tuple of path components

Output:

readme.txt
readme
.txt
documents
('documents', 'readme.txt')

When / why to use it#

  • Any time you manipulate file paths β€” pathlib is cleaner and more readable than os.path.join().
  • Reading and writing files without managing file handles.
  • Recursive directory searches with rglob("*.py").
  • Cross-platform code: Path uses the right separator on every OS.

[!TIP] Prefer Path over str for all path values in new code. If a library requires a string, wrap: str(p) or use p.as_posix() for forward-slash strings.

Common pitfalls#

[!WARNING] / operator builds paths, not divides β€” Path("/home") / "user" / "file.txt" is path concatenation. If you pass an absolute path as the right operand it replaces everything to the left: Path("/home") / "/etc/passwd" β†’ Path("/etc/passwd").

[!WARNING] Path.open() vs open() β€” both work, but path.read_text() / path.write_text() are shorter for simple file reads/writes and handle encoding arguments directly.

Special paths#

from pathlib import Path

print(Path.cwd())        # current working directory
print(Path.home())       # home directory (~)
print(Path("/").root)    # "/"

Output:

/home/user/myproject
/home/user
/

Reading and writing#

from pathlib import Path

p = Path("notes.txt")

# Write text (creates or overwrites)
p.write_text("Hello\nWorld\n", encoding="utf-8")

# Read text
content = p.read_text(encoding="utf-8")
print(repr(content))

# Append (open explicitly)
with p.open("a") as f:
    f.write("More text\n")

# Binary
p.write_bytes(b"\x89PNG\r\n")
data = p.read_bytes()
print(len(data), "bytes")

Output:

'Hello\nWorld\n'
6 bytes

Richer example β€” directory operations and glob#

from pathlib import Path

base = Path("/tmp/demo_project")

# Create a directory tree
(base / "src").mkdir(parents=True, exist_ok=True)
(base / "tests").mkdir(parents=True, exist_ok=True)

# Write some files
(base / "src" / "app.py").write_text("print('app')\n")
(base / "src" / "utils.py").write_text("# utilities\n")
(base / "tests" / "test_app.py").write_text("# tests\n")
(base / "README.md").write_text("# Demo\n")

# Find all Python files recursively
py_files = sorted(base.rglob("*.py"))
print("Python files:")
for f in py_files:
    print(f"  {f.relative_to(base)}  ({f.stat().st_size} bytes)")

# Find only in src/ (non-recursive)
src_files = sorted((base / "src").glob("*.py"))
print("\nSrc files:")
for f in src_files:
    print(f"  {f.name}")

Output:

Python files:
  src/app.py  (13 bytes)
  src/utils.py  (14 bytes)
  tests/test_app.py  (9 bytes)

Src files:
  app.py
  utils.py

Checking existence and type#

from pathlib import Path

p = Path("/tmp/demo_project/src/app.py")

print(p.exists())     # True if path exists (any type)
print(p.is_file())    # True if regular file
print(p.is_dir())     # True if directory
print(p.is_symlink()) # True if symlink

Output:

True
True
False
False

Quick reference#

TaskCode
Build pathPath("dir") / "sub" / "file.txt"
Absolute pathp.resolve()
Home dirPath.home()
Current dirPath.cwd()
Read textp.read_text(encoding="utf-8")
Write textp.write_text("content")
Read bytesp.read_bytes()
Create dirp.mkdir(parents=True, exist_ok=True)
List dirlist(p.iterdir())
Glob (non-recursive)list(p.glob("*.py"))
Glob (recursive)list(p.rglob("*.py"))
File sizep.stat().st_size
Rename / movep.rename(new_path)
Copy (no method β€” use)shutil.copy2(src, dst)
Delete filep.unlink()
Delete empty dirp.rmdir()
Delete treeshutil.rmtree(p)
Change extensionp.with_suffix(".txt")
Change namep.with_name("other.txt")
Change stemp.with_stem("other")
Relative pathp.relative_to(base)
Parent dirsp.parents[0], p.parents[1], …