Работа с папками и файлами в Python
Управление каталогами и файлами в Python сводится к нескольким задачам: узнать и сменить рабочую папку, получить список содержимого, создать, переименовать, скопировать и удалить файлы и каталоги. Для каждой задачи ниже показан вариант на модулях os и shutil и равноценный вариант на pathlib, а также разобраны ошибки, которые возникают при работе с путями.

Содержание статьи
Текущий рабочий каталог
Рабочий каталог - это папка, относительно которой интерпретатор разрешает относительные пути. Это не обязательно папка со скриптом: если запустить python tools/run.py из корня проекта, рабочим каталогом будет корень. Узнать его можно функцией os.getcwd(), сменить через os.chdir().
import os
from pathlib import Path
print(os.getcwd()) # C:\Projects\demo
os.chdir("data") # перейти во вложенную папку
print(Path.cwd()) # C:\Projects\demo\data
script_dir = Path(__file__).resolve().parent # папка самого скриптаМенять рабочий каталог внутри программы стоит осторожно: это глобальное состояние, и остальной код начнет искать файлы в другом месте. Надежнее строить пути от папки скрипта через Path(__file__).parent. Пути в Windows записывайте через прямой слеш или сырую строку r"C:\Python\test", иначе \t превратится в табуляцию.
Список файлов и папок
os.listdir() возвращает список имен в каталоге, без пути и без сортировки. Если аргумент не передан, берется текущий каталог. Чтобы отличить файлы от папок, удобнее os.scandir() или Path.iterdir(): они возвращают объекты с методами is_file() и is_dir().
import os
from pathlib import Path
print(os.listdir(".")) # ['data', 'main.py', 'readme.txt']
for entry in os.scandir("."):
kind = "папка" if entry.is_dir() else "файл"
print(kind, entry.name)
files = sorted(p.name for p in Path(".").iterdir() if p.is_file())Для поиска по шаблону используют Path.glob("*.txt"), а для рекурсивного поиска во всех подпапках Path.rglob("*.txt"). Подробнее о шаблонах написано в статье про модуль glob.
Обход всего дерева через os.walk
Функция os.walk() проходит по каталогу и всем вложенным папкам. На каждом шаге она выдает кортеж из трех элементов: путь к текущей папке, список подпапок и список файлов.
import os
total = 0
for folder, dirs, files in os.walk("project"):
dirs[:] = [d for d in dirs if d != ".git"] # не заходить в .git
for name in files:
total += os.path.getsize(os.path.join(folder, name))
print(f"Размер: {total / 1024:.1f} КБ")Если изменить список dirs на месте, os.walk() пропустит удаленные из него папки. Так исключают служебные каталоги вроде .git или __pycache__.
Создание каталогов
os.mkdir() создает одну папку и выбрасывает FileNotFoundError, если родительской папки нет, и FileExistsError, если папка уже есть. os.makedirs() создает всю цепочку вложенных папок, а с параметром exist_ok=True не ругается на существующую.
import os
from pathlib import Path
os.mkdir("reports")
os.makedirs("reports/2024/05", exist_ok=True)
Path("logs/archive").mkdir(parents=True, exist_ok=True) # то же на pathlibВременную папку, которая сама удалится после работы, дает tempfile.TemporaryDirectory() из стандартной библиотеки. Ее используют в тестах и при распаковке архивов.
Переименование, перемещение и копирование
os.rename(old, new) переименовывает файл или папку и может перенести их в другой каталог на том же диске. В Windows он выдаст FileExistsError, если файл с новым именем уже существует. Для замены существующего файла на любой системе используют os.replace().
import os
import shutil
os.rename("draft.txt", "final.txt")
os.replace("new_config.ini", "config.ini") # перезапишет config.ini
shutil.move("final.txt", "archive/final.txt") # работает и между дисками
shutil.copy2("config.ini", "backup/") # копия с датой изменения
shutil.copytree("src", "src_backup", dirs_exist_ok=True)Копирование в модуле os не реализовано, для этого служит shutil. copy() переносит содержимое и права доступа, copy2() дополнительно сохраняет время изменения, copytree() копирует папку целиком. Остальные возможности модуля описаны в статье про shutil.
Удаление файлов и папок
Удаление необратимо: файлы не попадают в корзину. Поэтому перед удалением полезно проверить путь и вывести его на экран хотя бы при первом запуске скрипта.
| Что удалить | os и shutil | pathlib |
|---|---|---|
| файл | os.remove(path) | Path(path).unlink() |
| пустую папку | os.rmdir(path) | Path(path).rmdir() |
| папку с содержимым | shutil.rmtree(path) | нет аналога |
from pathlib import Path
import shutil
Path("tmp.log").unlink(missing_ok=True) # без ошибки, если файла нет
for old in Path("cache").glob("*.tmp"):
old.unlink()
shutil.rmtree("build", ignore_errors=True)os.rmdir() откажется удалять непустую папку и выбросит OSError. Это защита от случайной потери данных, и rmtree() ее обходит, поэтому путь для нее лучше собирать из проверенных частей, а не из пользовательского ввода.
Проверки и частые ошибки
Перед операцией часто нужно убедиться, что путь существует и указывает на нужный тип объекта. Для этого есть os.path.exists(), os.path.isfile(), os.path.isdir() и их аналоги в pathlib: exists(), is_file(), is_dir().
FileNotFoundError: неверный путь или не тот рабочий каталог. ВыведитеPath(путь).resolve(), чтобы увидеть, где на самом деле ищется файл.PermissionError: файл открыт в другой программе (частая ситуация в Windows) или не хватает прав.IsADirectoryError: попытка открыть или удалить папку функцией для файлов.- Склеивание путей через
+ломается на разделителях. Используйтеos.path.join()или оператор/уPath.
Проверка через exists() перед действием не защищает от гонки, если файл может исчезнуть в промежутке. В таких случаях выполняют действие сразу и перехватывают исключение. Сравнение двух подходов к путям подробно разобрано в статье о pathlib, а чтение содержимого файлов описано в материале о чтении и записи файлов.
Частые вопросы
Ответы на вопросы, которые возникают при работе с папками из Python.
Как получить список только файлов без папок?
Отфильтруйте содержимое: [p for p in Path("dir").iterdir() if p.is_file()]. Для вариантов с os.listdir() проверяйте каждый элемент через os.path.isfile(os.path.join(dir, name)).
Как создать папку, только если ее еще нет?
Вызовите os.makedirs(path, exist_ok=True) или Path(path).mkdir(parents=True, exist_ok=True). Отдельная проверка exists() не нужна.
Как узнать дату изменения файла?
os.path.getmtime(path) вернет время в секундах от начала эпохи. Перевести его в дату можно через datetime.fromtimestamp().
Чем os отличается от pathlib?
os работает со строками и функциями, pathlib с объектами путей и методами. Возможности почти одинаковые, но код на pathlib короче и одинаково работает в Windows и Linux. Для копирования и рекурсивного удаления оба подхода используют shutil.