Python's pathlib module handles files and folders through Path objects. You build paths with /, create folders with mkdir(), list them with iterdir() and glob(), and move or delete entries with rename(), unlink() and rmdir():
from pathlib import Path
archive = Path("photos") / "2026"
archive.mkdir(parents=True, exist_ok=True)
for image in sorted(archive.glob("*.png")):
print(image.name)This lesson covers each step and the safety habits that matter when a program changes the file system: a typo in a delete command has no undo. The examples organise a photo archive, and every runnable demonstration works inside a temporary folder that is removed afterwards.
Path objects
A Path represents a location on disk. Joining with / uses the correct separator on every operating system, so the same code works on Windows (\) and macOS or Linux (/):
from pathlib import Path
project = Path("projects") / "regatta" / "photos"
print(project.name, project.parent, project.suffix) # photos projects/regatta (empty)
image = project / "finish-line.png"
print(image.stem, image.suffix) # finish-line .pngUseful attributes and methods include .name, .stem, .suffix, .parent, .exists(), .is_file(), .is_dir(), .resolve() (absolute path) and Path.home() and Path.cwd().
Creating a Path object doesn't touch the disk; it's only a description of a location. Path("missing.txt") is a perfectly valid object for a file that doesn't exist. Check with .exists(), or just try the operation and handle the error. Older code uses os.path.join() and string paths; they still work, and most functions accept either form.
Directory creation
mkdir() creates a folder. Two options make it practical:
archive = Path("archive") / "2026" / "july"
archive.mkdir(parents=True, exist_ok=True)parents=Truecreates any missing parent folders, likemkdir -p.exist_ok=Truemakes it succeed quietly if the folder is already there, so the script can run repeatedly.
Without exist_ok, an existing folder raises FileExistsError. Even with it, if a file (not a folder) already has that name, mkdir still fails, because a file can't be turned into a directory.
For scratch work, tempfile.TemporaryDirectory() creates a unique folder and deletes it, with everything inside, when the with block ends. It's the safest place for tests, experiments and intermediate files.
Traversal
Three methods list a folder's contents:
| Method | Returns |
|---|---|
folder.iterdir() | every immediate child (files and folders) |
folder.glob("*.png") | children matching a pattern |
folder.rglob("*.png") | matches in this folder and all sub-folders |
pngs = sorted(p for p in archive.glob("*.png") if p.is_file())
total = sum(p.stat().st_size for p in pngs)Patterns use * (any characters), ? (one character) and [abc] (one of a set); ** in glob() means "any depth of folders". All three return iterators, and the order is not guaranteed: it depends on the operating system and file system. Sort the results whenever order matters for display, reproducibility or tests.
os.walk() is the older alternative that yields each folder with lists of its sub-folders and files, which is still handy when you need to process a tree folder by folder.
Moves and deletion
| Task | pathlib | Notes |
|---|---|---|
| Rename or move a file | p.rename(target) | returns the new path |
| Replace an existing target | p.replace(target) | overwrites without asking |
| Delete a file | p.unlink(missing_ok=True) | permanent |
| Delete an empty folder | p.rmdir() | fails if not empty |
| Delete a folder tree | shutil.rmtree(p) | deletes everything inside |
| Copy a file / tree | shutil.copy2(p, t) / shutil.copytree(p, t) | keeps metadata |
rename() may fail across different drives; shutil.move() handles that case by copying and deleting.
Before any recursive delete, check the target: print it, make sure it's inside the folder you expect (target.resolve().is_relative_to(base.resolve()) in Python 3.9+), and never build it from unchecked user input. Scripts that delete things are often run with a "dry run" option first, which prints what would be removed without doing it.
Independent practice and a worked check
Task: create and rename sample files inside TemporaryDirectory, then explain the difference between unlink and rmdir.
from pathlib import Path
from tempfile import TemporaryDirectory
with TemporaryDirectory() as tmp:
folder = Path(tmp) / "drafts"
folder.mkdir()
draft = folder / "draft.txt"
draft.write_text("v1", encoding="utf-8")
final = draft.rename(folder / "final.txt")
print(sorted(p.name for p in folder.iterdir())) # ['final.txt']
final.unlink() # removes a file
folder.rmdir() # removes an empty directory
print(folder.exists()) # Falseunlink() deletes a file; rmdir() deletes a folder only when it's empty, which is why the file had to go first. For the worked check: does creating a Path object create a filesystem entry? No. Only methods such as mkdir(), touch() or write_text() change the disk.
Reference and next practice
The pathlib documentation and shutil documentation cover every operation, including the table mapping os.path functions to pathlib. Continue with the standard library tour and environment configuration. Practise with the Python exercises and Python quiz.
File and Directory Operations Summary
- Path objects: pathlib.Path expresses filesystem paths and joins components with the slash operator.
- Directory creation: mkdir can create parents and tolerate an already existing directory when configured.
- Traversal: iterdir lists immediate children while glob can select matching names.
- Moves and deletion: rename moves a path while unlink removes files and rmdir removes empty directories.
File and Directory Operations Examples
Each example shows the code, the output it produces and what each part does.
File and Directory Operations in a small program
from pathlib import Path
from tempfile import TemporaryDirectory
with TemporaryDirectory() as folder:
archive = Path(folder) / "photos"
archive.mkdir()
(archive / "sample.txt").write_text("Frame 8", encoding="utf-8")
print(sorted(p.name for p in archive.iterdir()))['sample.txt']Line-by-line explanation
- Line 3
TemporaryDirectory()creates a fresh folder that is removed at the end of the block. - Lines 4 to 5A
Pathis built with/and the folder is created withmkdir(). - Line 6
write_text()creates a file inside it. - Line 7
iterdir()lists the folder; sorting the names makes the output predictable.
Organising an archive by file type
from pathlib import Path
from tempfile import TemporaryDirectory
with TemporaryDirectory() as tmp:
inbox = Path(tmp) / "inbox"
inbox.mkdir()
for name in ["start.png", "finish.png", "notes.txt", "crew.jpg"]:
(inbox / name).write_text("demo", encoding="utf-8")
for item in sorted(inbox.iterdir()):
target_dir = Path(tmp) / "sorted" / item.suffix.lstrip(".")
target_dir.mkdir(parents=True, exist_ok=True)
item.rename(target_dir / item.name)
for path in sorted(Path(tmp).rglob("*")):
if path.is_file():
print(path.relative_to(tmp))
print("Inbox empty:", not any(inbox.iterdir()))
inbox.rmdir()
print("Inbox removed:", not inbox.exists())sorted/jpg/crew.jpg
sorted/png/finish.png
sorted/png/start.png
sorted/txt/notes.txt
Inbox empty: True
Inbox removed: TrueLine-by-line explanation
- Line 11The extension, without its dot, names the destination folder.
- Line 12
parents=True, exist_ok=Truecreates "sorted/png" the first time and ignores it afterwards. - Lines 15 to 17
rglob("*")walks every sub-folder;relative_to()shortens the printed paths. - Line 19
rmdir()only succeeds because the folder is now empty.
Common File and Directory Operations Mistakes
Building paths with string concatenation
folder = "photos"
path = folder + "/" + "2026" + "/" + "start.png" # wrong separator on some systems, easy to double upfrom pathlib import Path
path = Path("photos") / "2026" / "start.png"Deleting a tree without checking the target
import shutil
shutil.rmtree(user_supplied_path) # could be anything, including your home folderimport shutil
from pathlib import Path
base = Path("archive").resolve()
target = (base / user_supplied_name).resolve()
if target.is_relative_to(base) and target != base:
shutil.rmtree(target)Restrict deletions to a known folder and resolve the path first so .. can't escape it.
File and Directory Operations in Real Projects
File system automation
Organising downloads, archiving logs by month, generating output folders for each report, cleaning build directories and backing up project files are classic Python automation tasks. pathlib makes these scripts short and portable, and shutil adds copying, moving and archiving (shutil.make_archive() creates ZIP files).
Professional tools add safety: they log every change, support a dry-run mode, refuse to operate outside a configured root folder, and process files in sorted order so runs are reproducible. Test suites use TemporaryDirectory or pytest's tmp_path fixture so tests never touch real user files.
File and Directory Operations FAQ
Should I use pathlib or os.path?
Use pathlib for new code. It is object-oriented, reads more naturally (folder / "file.txt") and groups related operations on one object. os.path still works and appears in older code; most standard functions accept Path objects as well as strings.
How do I list all files in a directory in Python?
[p for p in Path("folder").iterdir() if p.is_file()] lists the files in one folder. Use Path("folder").rglob("*") to include sub-folders, and glob("*.csv") to filter by pattern. Sort the result if order matters.
How do I create a folder only if it doesn't exist?
Path("folder").mkdir(parents=True, exist_ok=True). It creates missing parents and does nothing if the folder already exists.
How do I delete a folder that isn't empty?
shutil.rmtree(path) deletes the folder and everything inside it, permanently and without confirmation. Path.rmdir() only removes empty folders. Double-check the path before calling rmtree.
How do I get a file's extension in Python?
Path("photo.final.png").suffix returns ".png", and .suffixes returns all of them, ['.final', '.png']. .stem gives the name without the last extension.
File and Directory Operations Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
Does creating a Path object create a filesystem entry?
Which review concern is valid for file and directory operations?
File and Directory Operations Quiz
2 questions to check what you learned.
Does creating a Path object create a filesystem entry?
- Yes
- No
- Python ignores the distinction
Which statement correctly describes traversal?
- iterdir lists immediate children while glob can select matching names.
- Only the example output matters, not the rule
- Every operation has the same behavior