Working with paths¶
The pathlib module is the modern, object-oriented way to work with file system paths in Python. It provides a cleaner and more intuitive interface than string-based path manipulation, and it works consistently across different operating systems.
Time commitment: 15–20 minutes
Prerequisites:
- Basic Python knowledge
- Completion of Reading files and Writing files
Learning objectives¶
By the end of this tutorial, you will be able to:
- Create
Pathobjects from strings - Combine paths using the
/operator - Access path components (name, stem, suffix, parent)
- Check whether files and directories exist
- List directory contents
- Create and remove directories
Creating Path objects¶
You create a Path object by passing a string to the Path constructor. The Path class also provides class methods for common locations.
from pathlib import Path
current = Path(".")
home = Path.home()
file_path = Path("data") / "reports" / "summary.txt"
print(f"Current directory: {current.resolve()}")
print(f"Home directory: {home}")
print(f"File path: {file_path}")
Combining paths with the / operator¶
The / operator joins path components together. This is platform-independent – it uses the correct separator for each operating system.
from pathlib import Path
base = Path("projects")
project = base / "my-app"
config = project / "config" / "settings.txt"
print(config)
This is much cleaner and safer than concatenating strings with "/" or "\\", which can break on different platforms.
Path components¶
A Path object gives you easy access to the different parts of a file path.
from pathlib import Path
path = Path("documents") / "reports" / "annual-report.pdf"
print(f"Full path: {path}")
print(f"Name: {path.name}")
print(f"Stem: {path.stem}")
print(f"Suffix: {path.suffix}")
print(f"Parent: {path.parent}")
print(f"Parts: {path.parts}")
Here is what each property returns:
name-- the final component of the path (file name with extension)stem-- the file name without the extensionsuffix-- the file extension (including the dot)parent-- the directory containing the fileparts-- a tuple of all the path components
Checking whether files and directories exist¶
The Path class provides methods to check the status of a path.
from pathlib import Path
Path("test-file.txt").write_text("Hello", encoding="utf-8")
print(f"test-file.txt exists: {Path('test-file.txt').exists()}")
print(f"test-file.txt is a file: {Path('test-file.txt').is_file()}")
print(f"test-file.txt is a directory: {Path('test-file.txt').is_dir()}")
print(f"nonexistent.txt exists: {Path('nonexistent.txt').exists()}")
Resolving and absolute paths¶
The resolve() method converts a relative path to an absolute path. The is_absolute() method tells you whether a path is already absolute.
from pathlib import Path
relative = Path("test-file.txt")
absolute = relative.resolve()
print(f"Relative: {relative}")
print(f"Absolute: {absolute}")
print(f"Is absolute: {absolute.is_absolute()}")
Creating directories¶
The mkdir() method creates a directory. Use parents=True to create all intermediate directories, and exist_ok=True to avoid errors if the directory already exists.
from pathlib import Path
Path("example-dir").mkdir(exist_ok=True)
Path("nested/sub/dir").mkdir(parents=True, exist_ok=True)
print(f"example-dir exists: {Path('example-dir').exists()}")
print(f"nested/sub/dir exists: {Path('nested/sub/dir').exists()}")
Listing directory contents¶
The iterdir() method iterates over all items in a directory. The glob() method filters items by a pattern.
from pathlib import Path
# Create some files in the example directory
(Path("example-dir") / "file1.txt").write_text("Content 1", encoding="utf-8")
(Path("example-dir") / "file2.txt").write_text("Content 2", encoding="utf-8")
(Path("example-dir") / "data.csv").write_text("a,b,c", encoding="utf-8")
print("All items in example-dir:")
for item in sorted(Path("example-dir").iterdir()):
print(f" {item.name} (file: {item.is_file()})")
print("\nOnly .txt files:")
for item in sorted(Path("example-dir").glob("*.txt")):
print(f" {item.name}")
Renaming and deleting¶
The rename() method renames a file or directory, and the unlink() method deletes a file.
from pathlib import Path
Path("old-name.txt").write_text("Some content", encoding="utf-8")
Path("old-name.txt").rename("new-name.txt")
print(f"old-name.txt exists: {Path('old-name.txt').exists()}")
print(f"new-name.txt exists: {Path('new-name.txt').exists()}")
Path("new-name.txt").unlink()
print(f"After deletion: {Path('new-name.txt').exists()}")
Changing path components¶
The with_name(), with_stem(), and with_suffix() methods return new Path objects with modified components.
from pathlib import Path
path = Path("documents") / "report.txt"
print(f"Original: {path}")
print(f"Change suffix: {path.with_suffix('.md')}")
print(f"Change name: {path.with_name('notes.txt')}")
print(f"Change stem: {path.with_stem('summary')}")
Practical example: organising files by extension¶
Here is a practical function that groups files in a directory by their extension.
from pathlib import Path
def list_files_by_extension(directory: str | Path) -> dict[str, list[str]]:
"""Group files in a directory by their extension.
Args:
directory: The path to the directory to scan.
Returns:
A dictionary mapping extensions to lists of file names.
"""
path = Path(directory)
result: dict[str, list[str]] = {}
for item in path.iterdir():
if item.is_file():
ext = item.suffix if item.suffix else "(no extension)"
result.setdefault(ext, []).append(item.name)
return result
grouped = list_files_by_extension("example-dir")
for ext, files in sorted(grouped.items()):
print(f"{ext}: {files}")
Exercises¶
Try these exercises to practise what you have learned.
Exercise 1: Write a function that takes a directory path and returns the total number of files (not directories) in it.
Exercise 2: Write a function that takes a file path and returns a new path with a different extension (for example, change .txt to .md).
Exercise 3: Create a directory structure project/src and project/tests, create a sample .py file in each, then list all .py files recursively using rglob().
Solutions¶
from pathlib import Path
def count_files(directory: str | Path) -> int:
"""Count the number of files in a directory.
Args:
directory: The path to the directory to count files in.
Returns:
The number of files (not directories) in the directory.
"""
path = Path(directory)
return sum(1 for item in path.iterdir() if item.is_file())
print(f"Files in example-dir: {count_files('example-dir')}")
from pathlib import Path
def change_extension(filepath: str | Path, new_extension: str) -> Path:
"""Return a new path with a different file extension.
Args:
filepath: The original file path.
new_extension: The new extension (including the dot).
Returns:
A new Path object with the extension changed.
"""
return Path(filepath).with_suffix(new_extension)
original = Path("documents/report.txt")
changed = change_extension(original, ".md")
print(f"Original: {original}")
print(f"Changed: {changed}")
from pathlib import Path
Path("project/src").mkdir(parents=True, exist_ok=True)
Path("project/tests").mkdir(parents=True, exist_ok=True)
(Path("project/src") / "main.py").write_text(
'print("Hello")\n', encoding="utf-8"
)
(Path("project/tests") / "test_main.py").write_text(
"import unittest\n", encoding="utf-8"
)
print("All .py files:")
for py_file in sorted(Path("project").rglob("*.py")):
print(f" {py_file}")
import shutil
from pathlib import Path
Path("test-file.txt").unlink(missing_ok=True)
for d in ["example-dir", "nested", "project"]:
if Path(d).exists():
shutil.rmtree(d)
print("Temporary files and directories removed.")
Summary¶
In this tutorial, you learned how to use pathlib.Path for file and directory operations. Here are the key takeaways:
pathlib.Pathprovides an object-oriented way to work with file paths- The
/operator joins paths in a platform-independent way - Path components (
name,stem,suffix,parent) let you inspect paths easily exists(),is_file(), andis_dir()check the status of a pathmkdir()creates directories (useparents=Truefor nested directories)iterdir()andglob()list directory contentswith_suffix(),with_name(), andwith_stem()create modified path copies
In the next tutorial, you will learn how to work with CSV and JSON files.