Управление каталогами в Python: все способы и примеры
Работа с директориями файлов в Python
Как эффективно и безопасно работать с директориями в современном Python?
Наилучшим решением для большинства задач является использование модуля pathlib, входящего в стандартную библиотеку начиная с Python 3.4. Он предоставляет объектно-ориентированный интерфейс для путей файловой системы, избавляя от ручной работы со строками и обеспечивая кроссплатформенность.
from pathlib import Path
# Создание объекта пути к директории
dir_path = Path('/home/user/documents')
# Проверка существования
if dir_path.exists():
print(f'Директория {dir_path} существует')
# Создание новой директории (включая родительские, если нужно)
dir_path.mkdir(parents=True, exist_ok=True)
# Получение списка содержимого
for item in dir_path.iterdir():
print(item.name)
Path python 2 (путь к python 2)
Пояснение:
- Path() создаёт объект, представляющий путь.
- exists() проверяет, существует ли директория.
- mkdir(parents=True, exist_ok=True) создаёт директорию, не вызывая ошибку, если она уже есть.
- iterdir() возвращает генератор всех элементов внутри директории.
Типичные ошибки:
- Ошибка FileNotFoundError при попытке открыть файл из несуществующей директории - решается предварительной проверкой exists() или созданием через mkdir().
- Ошибка PermissionError при недостатке прав - обрабатывать через try/except.
- Пути с обратной косой чертой (\ на Windows) - pathlib автоматически адаптирует разделители, но если передать строку вручную, лучше использовать Path(r'C:\Users\...') или '/'.join().
Как проверить, является ли объект директорией, с помощью модуля os.path?
Классический способ, подходящий для кода, поддерживающего Python 2.x или уже использующего модуль os. Функция os.path.isdir() возвращает True, если путь указывает на существующую директорию.
import os.path
dir_path = '/tmp/my_folder'
if os.path.isdir(dir_path):
print('Это директория')
else:
print('Такого пути нет или это файл')
Python find directory (поиск директории)
Используется в скриптах, где уже много кода на os или требуется совместимость с очень старыми версиями.
Как получить список всех файлов и папок в директории, не используя pathlib?
Функция os.listdir() возвращает список имён всех элементов в указанной директории. Для получения полных путей удобно комбинировать с os.path.join().
import os
dir_path = '/tmp'
items = os.listdir(dir_path)
for name in items:
full_path = os.path.join(dir_path, name)
if os.path.isdir(full_path):
print(f'{name} - папка')
else:
print(f'{name} - файл')
File does not exist python (проверка существования файла)
Недостаток: os.listdir() возвращает только имена, не различает файлы и папки (требуется дополнительная проверка). Также не рекурсивен.
Как эффективно обойти содержимое большой директории с помощью os.scandir?
Функция os.scandir() возвращает итератор объектов DirEntry, каждый из которых содержит имя и информацию о типе (файл/папка) без дополнительных системных вызовов. Это быстрее, чем os.listdir() с последующими проверками.
import os
with os.scandir('/tmp') as entries:
for entry in entries:
if entry.is_dir():
print(f'Папка: {entry.name}')
else:
print(f'Файл: {entry.name}')
Python path configuration (конфигурация путей python)
Проблемы:
- При использовании os.scandir() с итератором важно вызывать entry.stat() только когда это необходимо, чтобы не получить задержку.
- Закрытие итератора через with или явный вызов .close() - иначе ресурсы могут не освободиться на Windows.
Как найти файлы по шаблону (glob) в директории и её поддиректориях?
Модуль glob предоставляет функции для поиска путей, соответствующих заданному шаблону (с символами *, ?, []). glob.glob() возвращает список строк, glob.iglob() - итератор.
import glob
# Найти все .txt файлы непосредственно в /tmp
for path in glob.glob('/tmp/*.txt'):
print(path)
# Рекурсивный поиск во всех поддиректориях (Python 3.5+)
for path in glob.glob('/tmp/**/*.txt', recursive=True):
print(path)
Python installation path (путь установки python)
Применяется, когда нужно отобрать файлы по расширению или имени без написания обхода вручную.
Как рекурсивно обойти все поддиректории для выполнения действий над каждым файлом?
Функция os.walk() генерирует кортежи (путь к текущей папке, список подпапок, список файлов) для каждой директории, начиная с указанной. Позволяет выполнять произвольные действия.
import os
for root, dirs, files in os.walk('/tmp'):
for file in files:
full_path = os.path.join(root, file)
print(full_path)
Python path exists (проверка существования пути в python)
Подходит для операций, требующих обхода всего дерева (удаление, копирование, подсчёт).
Как создать временную директорию для временных файлов?
Модуль tempfile предоставляет функцию tempfile.mkdtemp(), которая создаёт уникальную временную папку и возвращает её путь.
import tempfile
with tempfile.TemporaryDirectory() as tmpdir:
print(f'Временная директория: {tmpdir}')
# Работа с файлами внутри tmpdir...
# после выхода из блока with директория автоматически удаляется
Типичные ошибки:
- Забыть закрыть контекстный менеджер - временная папка останется (если используется mkdtemp() без TemporaryDirectory).
- При ручном удалении временных папок возможны ошибки, если файлы внутри заняты другим процессом.
Расширенные примеры работы с директориями в Python
Ниже приведены более сложные и неочевидные сценарии использования модулей pathlib, os, shutil и других.
Пример 1. Рекурсивное копирование всей структуры директорий с фильтрацией по расширению
import shutil
from pathlib import Path
def copy_filtered(src_base: Path, dst_base: Path, extensions: set):
for src_path in src_base.rglob('*'):
if src_path.is_file() and src_path.suffix in extensions:
# Формируем относительный путь
rel_path = src_path.relative_to(src_base)
dst_path = dst_base / rel_path
dst_path.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(src_path, dst_path)
src = Path('/data/source')
dst = Path('/data/backup')
copy_filtered(src, dst, {'.jpg', '.png'})
# Результат: в /data/backup будут скопированы только .jpg и .png файлы с сохранением структуры папок
Пояснение: rglob('*') рекурсивно обходит все файлы и папки. relative_to() получает путь относительно исходной базы. shutil.copy2() копирует файл с метаданными.
Пример 2. Получение размера директории (суммарного объёма всех файлов) с помощью pathlib
from pathlib import Path
def get_dir_size(path: Path) -> int:
total = 0
for entry in path.rglob('*'):
if entry.is_file():
total += entry.stat().st_size
return total
dir_path = Path('/var/log')
size_bytes = get_dir_size(dir_path)
print(f'Размер директории: {size_bytes} байт ({size_bytes / (1024**2):.2f} МБ)')
# Размер директории: 1048576 байт (1.00 МБ)
Пояснение: используется rglob('*') для обхода, stat().st_size возвращает размер файла. Внимание: при большом количестве файлов может быть медленно; альтернатива - os.walk() с os.path.getsize().
Пример 3. Синхронизация двух директорий с сохранением времени модификации
import hashlib
from pathlib import Path
import shutil
def file_hash(path: Path, chunk_size=8192) -> str:
h = hashlib.sha256()
with open(path, 'rb') as f:
for chunk in iter(lambda: f.read(chunk_size), b''):
h.update(chunk)
return h.hexdigest()
def sync_dirs(src: Path, dst: Path):
if not dst.exists():
dst.mkdir(parents=True)
for src_path in src.rglob('*'):
rel = src_path.relative_to(src)
dst_path = dst / rel
if src_path.is_dir():
dst_path.mkdir(exist_ok=True)
else:
if not dst_path.exists() or file_hash(src_path) != file_hash(dst_path):
shutil.copy2(src_path, dst_path)
print(f'Обновлён: {dst_path}')
sync_dirs(Path('/data/orig'), Path('/data/backup'))
# Обновлён: /data/backup/sub/file.txt # Обновлён: /data/backup/readme.md
Пояснение: проверка хеша гарантирует, что копируется только изменённое содержимое. shutil.copy2 сохраняет время модификации.
Пример 4. Использование os.scandir для быстрого подсчёта количества файлов и папок
import os
def count_entries(path: str) -> dict:
files = 0
dirs = 0
with os.scandir(path) as it:
for entry in it:
if entry.is_dir():
dirs += 1
else:
files += 1
return {'files': files, 'dirs': dirs}
result = count_entries('/usr/lib')
print(result)
# {'files': 342, 'dirs': 15}
Пример демонстрирует эффективность os.scandir - один системный вызов на запись.
Пример 5. Генератор путей с фильтром по дате изменения (pathlib)
from pathlib import Path
import time
def files_newer_than(path: Path, cutoff_timestamp: float):
for f in path.rglob('*'):
if f.is_file() and f.stat().st_mtime > cutoff_timestamp:
yield f
cutoff = time.time() - 86400 # 24 часа назад
dir_path = Path('/home/user')
for recent_file in files_newer_than(dir_path, cutoff):
print(recent_file)
# /home/user/notes.txt # /home/user/pictures/screenshot.png
Используется для поиска недавно изменённых файлов.
Пример 6. Создание глубокой иерархии директорий с одним вызовом mkdir
from pathlib import Path
# Создать папки project/data/images, если не существуют
Path('project/data/images').mkdir(parents=True, exist_ok=True)
# Проверка:
print(Path('project/data/images').exists()) # True
# True
Пояснение: parents=True создаёт все промежуточные недостающие папки. exist_ok=True предотвращает ошибку, если папка уже существует.
Пример 7. Рекурсивное удаление директории с использованием shutil.rmtree
import shutil
from pathlib import Path
dir_to_remove = Path('/tmp/old_project')
if dir_to_remove.exists():
shutil.rmtree(dir_to_remove)
print(f'Директория {dir_to_remove} удалена')
else:
print('Директория не существует')
# Директория /tmp/old_project удалена
Внимание: операция необратима, следует использовать с осторожностью.