Yesterday's receipts sit in a folder whose name changes every day. String paths break on the backslash; a Path object does not. How do you list the files without guessing the separator?
The pathlib module turns a path into an object with useful parts and methods. It handles Windows and POSIX separators, avoids most string concatenation bugs, and makes reading or writing a small text file a one-line operation. You will also replace path parts, list files deterministically, and reject directory traversal.
Build a path with /
The slash operator joins path parts. It is not division here; Path overloads / for joining. Keep each part as a plain string and let pathlib choose the separator for the current operating system.
from pathlib import Path
path = Path("data") / "scores.csv"
print(path.as_posix())
print(path.name)Join without string arithmetic
Inspect the parts
name is the final component, stem is the name without its last suffix, and suffix includes the dot. parent returns the containing directory. These properties are easier to read than a chain of split calls.
from pathlib import Path
path = Path("reports/2026-summary.txt")
print(path.stem)
print(path.suffix)
print(path.parent.as_posix())Names, suffixes, and parents
Read and write UTF-8 text
write_text creates or replaces a file, and read_text returns its contents. Pass encoding='utf-8' explicitly so the result does not depend on the machine locale. Clean up temporary files when an example is finished.
from pathlib import Path
path = Path("pypractice-note.txt")
path.write_text("hello\n", encoding="utf-8")
print(path.read_text(encoding="utf-8").strip())
path.unlink()A small text file
Change one part without string surgery
with_name and with_suffix replace one component while keeping the rest of the path. They are safer than replace() on the whole string, which can hit a directory name by accident. The original Path is immutable; each call returns a new one.
from pathlib import Path
path = Path("reports/2026-summary.txt")
print(path.with_name("2027-summary.txt").as_posix())
print(path.with_suffix(".md").as_posix())
print((path.parent / "archive" / path.name).as_posix())Replace a name, suffix, or parent
List files deterministically
glob() returns paths in filesystem order, which is not guaranteed to be sorted. Sort before showing results or comparing them. TemporaryDirectory gives the example a private directory and removes it even when the block exits with an error.
from pathlib import Path
from tempfile import TemporaryDirectory
with TemporaryDirectory() as tmp:
root = Path(tmp)
(root / "b.txt").write_text("tea", encoding="utf-8")
(root / "a.txt").write_text("latte", encoding="utf-8")
(root / "skip.md").write_text("x", encoding="utf-8")
for path in sorted(root.glob("*.txt")):
print(path.name, path.read_text(encoding="utf-8"))Sort the glob, not the filesystem
A form value is not a safe path
Path(raw).name keeps only the final component, so ../secret.txt cannot escape through that value. That is a start, not a complete security policy: use an allowlist, resolve the final path, and check that it stays inside the intended root before opening it.
from pathlib import Path
for raw in ["notes.txt", "../secret.txt", "/etc/passwd"]:
safe_name = Path(raw).name
print(raw, "->", safe_name)Strip directories before joining
Key points
- Path('folder') / 'file.txt' joins path parts safely.
- name, stem, suffix, and parent expose the path's parts.
- read_text and write_text default to the platform encoding unless you pass encoding.
- Use as_posix() when output must always use forward slashes.
- Use Path methods for components; never join untrusted text directly to a root.
Common mistakes
- Joining paths with string + and forgetting the separator.
- Printing a Path when a stable, portable string is required.
- Reading or writing text without specifying UTF-8.
- Using replace() on a whole path and accidentally changing a parent directory name.
Create Path('data') / 'scores.csv' and print its name, suffix, and parent as a POSIX path.
Try it yourself
Change the values, predict the output, then run the code. The Python runtime is downloaded the first time you press Run, so the first run can take a few seconds.
Practice this lesson
12 questions with immediate explanations.