Skip to content

os.path vs pathlib

Compare the traditional os.path module with the modern pathlib module for path operations.

Mental Model

os.path treats paths as plain strings and manipulates them with standalone functions (os.path.join, os.path.exists). pathlib wraps paths in Path objects with methods and operator overloads (path / "subdir", path.exists()). The result is the same — pathlib is just more readable and composable. New code should prefer pathlib; use os.path when interfacing with older APIs that expect strings.

os.path Approach

Traditional string-based path manipulation.

```python import os

Build path with os.path.join

parts = ['home', 'user', 'documents', 'file.txt'] path = os.path.join(*parts) print(f"os.path: {path}")

Extract components

print(f"dirname: {os.path.dirname(path)}") print(f"basename: {os.path.basename(path)}") print(f"split: {os.path.splitext(path)}")

Check file

import tempfile with tempfile.NamedTemporaryFile() as f: print(f"exists: {os.path.exists(f.name)}") ```

os.path: home/user/documents/file.txt dirname: home/user/documents basename: file.txt split: ('home/user/documents/file', '.txt') exists: True

pathlib Approach

Modern object-oriented path manipulation.

```python from pathlib import Path import tempfile

Create Path object

path = Path('home') / 'user' / 'documents' / 'file.txt' print(f"pathlib: {path}")

Access components

print(f"parent: {path.parent}") print(f"name: {path.name}") print(f"suffix: {path.suffix}") print(f"stem: {path.stem}")

Check file

with tempfile.NamedTemporaryFile() as f: p = Path(f.name) print(f"exists: {p.exists()}") print(f"is_file: {p.is_file()}") ```

pathlib: home/user/documents/file.txt parent: home/user/documents name: file.txt suffix: .txt stem: file exists: True is_file: True


Exercises

Exercise 1. Write two versions of a function build_output_path -- one using os.path and one using pathlib -- that takes a base directory, a subdirectory name, and a filename, and returns the full path with a .csv extension. Compare the readability of both approaches.

Solution to Exercise 1

```python import os from pathlib import Path

os.path version

def build_output_path_os(base, subdir, filename): return os.path.join(base, subdir, filename + ".csv")

pathlib version

def build_output_path_pathlib(base, subdir, filename): return Path(base) / subdir / (filename + ".csv")

Test

print(build_output_path_os("/data", "output", "results"))

/data/output/results.csv

print(build_output_path_pathlib("/data", "output", "results"))

/data/output/results.csv

```


Exercise 2. Using pathlib.Path, write a function find_all_python_files that takes a directory path and recursively finds all .py files. Return a list of Path objects sorted by name. Use the rglob method.

Solution to Exercise 2

```python from pathlib import Path

def find_all_python_files(directory): return sorted(Path(directory).rglob("*.py"), key=lambda p: p.name)

Test

files = find_all_python_files(".")

for f in files[:5]:

print(f)

```


Exercise 3. Write a function path_components that takes a file path string and returns a dictionary with "parent", "name", "stem", and "suffix" using pathlib.Path. For example, path_components("/home/user/data.csv") should return {"parent": "/home/user", "name": "data.csv", "stem": "data", "suffix": ".csv"}.

Solution to Exercise 3

```python from pathlib import Path

def path_components(path_str): p = Path(path_str) return { "parent": str(p.parent), "name": p.name, "stem": p.stem, "suffix": p.suffix, }

Test

print(path_components("/home/user/data.csv"))

{'parent': '/home/user', 'name': 'data.csv',

'stem': 'data', 'suffix': '.csv'}

```