Примеры идут от простого к сложному: сначала навигация и проверка объектов, затем создание/удаление и обход дерева, в конце — обработка ошибок, shutil и готовый CLI-скрипт. Лекция строится вокруг модуля os, но основным инструментом здесь взят современный pathlib.Path — рекомендация из теории и полное сравнение см. на странице «Старый vs Новый». Весь вывод в комментариях получен реальным запуском кода на Windows, а не придуман.
Пример 1. Текущая рабочая директория
Любой относительный путь отсчитывается от текущей рабочей директории. os.getcwd() — привычный способ из лекции, Path.cwd() — его современный аналог, который сразу возвращает объект Path, а не строку.
import os
from pathlib import Path
print(os.getcwd())
print(Path.cwd())
print(type(Path.cwd()))
# C:\Users\...\lesson50-demo
# C:\Users\...\lesson50-demo
# <class 'pathlib.WindowsPath'>
Что происходит: обе функции возвращают одно и то же место на диске, но os.getcwd() — обычная строка, а Path.cwd() — объект Path, у которого есть свои методы (.exists(), .iterdir() и другие, см. ниже). На Linux/macOS вместо WindowsPath будет PosixPath — pathlib сам выбирает нужный класс под операционную систему.
Пример 2. Проверка существования и типа объекта
Прежде чем открывать путь, часто нужно узнать: он вообще есть, и если да — это файл или папка.
from pathlib import Path
note = Path("lesson50_example2.txt")
note.write_text("demo", encoding="utf-8")
print(note.exists()) # True
print(note.is_file()) # True
print(note.is_dir()) # False
print(Path("lesson50_example2_missing.txt").exists()) # False
note.unlink() # удаляем файл — убираем за собой
print(note.exists()) # False
Что происходит: write_text создаёт файл и сразу закрывает его — не нужен отдельный open()/close(). exists(), is_file() и is_dir() — аналоги os.path.exists, os.path.isfile, os.path.isdir из справочника. unlink() — это Path-версия os.remove().
Пример 3. Создание и удаление файлов и папок
Собираем структуру «папка + файл в ней» и затем аккуратно её разбираем — именно так должен вести себя код, который может запускаться повторно.
from pathlib import Path
project_dir = Path("lesson50_example3_project")
project_dir.mkdir(parents=True, exist_ok=True) # создаст и родителей, не упадёт, если уже есть
config_file = project_dir / "config.txt"
config_file.write_text("debug = True\n", encoding="utf-8")
print(list(project_dir.iterdir()))
# [WindowsPath('lesson50_example3_project/config.txt')]
config_file.unlink() # сначала файл
project_dir.rmdir() # потом пустую папку
print(project_dir.exists()) # False
Что происходит: mkdir(parents=True, exist_ok=True) — аналог os.makedirs(path, exist_ok=True): создаёт все промежуточные папки и не выбрасывает ошибку, если путь уже существует. rmdir(), как и os.rmdir(), удаляет только пустую папку — с непустой он упадёт (см. Ошибка 2), поэтому файл внутри удаляется первым.
Пример 4. Содержимое директории
Чаще всего нужно не просто получить список имён, а сразу понять, что где — файл или подпапка.
import os
from pathlib import Path
demo_dir = Path("lesson50_example4_dir")
demo_dir.mkdir()
(demo_dir / "a.txt").write_text("a", encoding="utf-8")
(demo_dir / "b.txt").write_text("b", encoding="utf-8")
(demo_dir / "sub").mkdir()
print(os.listdir(demo_dir))
# ['a.txt', 'b.txt', 'sub']
for item in sorted(demo_dir.iterdir()):
label = "папка" if item.is_dir() else "файл"
print(f" {label}: {item.name}")
# файл: a.txt
# файл: b.txt
# папка: sub
(demo_dir / "a.txt").unlink()
(demo_dir / "b.txt").unlink()
(demo_dir / "sub").rmdir()
demo_dir.rmdir()
Что происходит: os.listdir() возвращает просто список имён-строк — чтобы узнать, файл это или папка, пришлось бы отдельно звать os.path.isdir() для каждого имени (см. пример из справочника). Path.iterdir() сразу отдаёт объекты Path, у которых есть .is_dir()/.is_file() — меньше кода на ту же задачу.
Пример 5. Платформонезависимая сборка путей
Windows разделяет части пути обратным слешем \, Linux и macOS — прямым /. Ручная конкатенация строк ломается при переносе кода между ОС (Ошибка 3), поэтому части пути всегда собирают через инструмент, а не через +.
import os
from pathlib import Path
base = Path("lesson50_example5_dir")
base.mkdir()
data_path = base / "data" / "input.txt"
print(data_path)
# lesson50_example5_dir\data\input.txt
same_as_string = os.path.join(str(base), "data", "input.txt")
print(same_as_string)
# lesson50_example5_dir\data\input.txt
print(str(data_path) == same_as_string) # True
base.rmdir()
Что происходит: оператор / у Path — это перегруженный оператор сложения путей: он сам подставит \ на Windows и / на Linux/macOS. os.path.join() из лекции делает то же самое, но возвращает строку, а не объект Path — результат в примере совпадает посимвольно.
Пример 6. Имя, расширение и родитель пути
Путь часто нужно разобрать на части: только имя файла, только расширение, только папку. Это работа с текстом самого пути — файл на диске для неё создавать не нужно.
from pathlib import Path
import os
report = Path("reports") / "2026" / "sales_january.CSV"
print(report.name) # sales_january.CSV
print(report.stem) # sales_january
print(report.suffix) # .CSV
print(report.parent) # reports\2026
path_str = str(report)
print(os.path.basename(path_str)) # sales_january.CSV
print(os.path.splitext(path_str)) # ('reports\\2026\\sales_january', '.CSV')
Что происходит: .name, .stem, .suffix и .parent — свойства, а не методы (без скобок). Из лекции это соответствует связке os.path.basename() + os.path.splitext() + os.path.dirname() (см. справочник): три отдельных вызова заменяются на одно разбиение пути.
Пример 7. Рекурсивный обход дерева директорий
Нужно найти все файлы с определённым расширением на всех уровнях вложенности — задача из Задания 2.
import os
from pathlib import Path
root_dir = Path("lesson50_example7_root")
(root_dir / "sub").mkdir(parents=True)
(root_dir / "notes.txt").write_text("root", encoding="utf-8")
(root_dir / "sub" / "data.txt").write_text("sub", encoding="utf-8")
(root_dir / "sub" / "image.png").write_text("fake", encoding="utf-8")
# pathlib: один вызов с маской имени
txt_files = sorted(root_dir.rglob("*.txt"))
for path in txt_files:
print(path)
# lesson50_example7_root\notes.txt
# lesson50_example7_root\sub\data.txt
# os.walk: даёт (root, dirs, files) на каждом уровне — больше контроля, больше кода
found = []
for current_dir, subdirs, files in os.walk(root_dir):
for file_name in files:
if file_name.endswith(".txt"):
found.append(os.path.join(current_dir, file_name))
print(sorted(found))
# ['lesson50_example7_root\\notes.txt', 'lesson50_example7_root\\sub\\data.txt']
(root_dir / "notes.txt").unlink()
(root_dir / "sub" / "data.txt").unlink()
(root_dir / "sub" / "image.png").unlink()
(root_dir / "sub").rmdir()
root_dir.rmdir()
Что происходит: rglob("*.txt") — это «recursive glob»: сразу отфильтрует файлы по маске имени во всём дереве. os.walk() из лекции универсальнее (можно на каждом уровне менять список dirs на лету, пропуская целые подпапки), но для простого «найти все файлы по маске» rglob короче.
Пример 8. Обработка ошибок файловой системы
Файл может исчезнуть, папка — уже существовать, а на файл в Windows иногда пытаются писать как в обычный файл, хотя это директория. Правильная реакция — поймать конкретное исключение, а не проверять всё заранее (см. Ошибка 4 и EAFP).
from pathlib import Path
# FileNotFoundError — читаем несуществующий файл
try:
Path("lesson50_example8_missing.txt").read_text(encoding="utf-8")
except FileNotFoundError as error:
print(f"Файл не найден: {error}")
# Файл не найден: [Errno 2] No such file or directory: 'lesson50_example8_missing.txt'
# FileExistsError — создаём папку, которая уже есть, без exist_ok
demo_err_dir = Path("lesson50_example8_dir")
demo_err_dir.mkdir()
try:
demo_err_dir.mkdir()
except FileExistsError as error:
print(f"Папка уже существует: {error}")
# Папка уже существует: [WinError 183] Невозможно создать файл, так как он
# уже существует: 'lesson50_example8_dir'
# PermissionError — пытаемся записать в путь, который на самом деле папка
try:
demo_err_dir.write_text("x", encoding="utf-8")
except PermissionError as error:
print(f"Permission denied: {error}")
except IsADirectoryError as error:
print(f"Is a directory: {error}")
# Permission denied: [Errno 13] Permission denied: 'lesson50_example8_dir'
demo_err_dir.rmdir()
WinError с сообщением на языке системы, на Linux/macOS для того же сценария (запись в путь, который является папкой) будет IsADirectoryError, а не PermissionError — код выше ловит оба варианта. Сверьте точные тексты для вашей системы с docs.python.org: OS exceptions.
Что происходит: все три исключения — подклассы OSError. FileNotFoundError и FileExistsError явно упомянуты в задаче — их вызывают os.chdir() на несуществующий путь (см. справочник) и os.mkdir()/Path.mkdir() без exist_ok=True. PermissionError лекция не разбирает — это дополнение курса.
Пример 9. shutil: копирование и перемещение
os умеет создавать, переименовывать и удалять — но не копировать папки целиком и не удалять непустые за один вызов. Для этого в стандартной библиотеке есть модуль shutil, упомянутый как альтернатива в Ошибке 2.
import shutil
from pathlib import Path
source_dir = Path("lesson50_example9_source")
source_dir.mkdir()
(source_dir / "report.txt").write_text("data", encoding="utf-8")
backup_dir = Path("lesson50_example9_backup")
shutil.copytree(source_dir, backup_dir) # рекурсивно копирует папку со всем содержимым
print(sorted(p.name for p in backup_dir.iterdir()))
# ['report.txt']
moved_dir = Path("lesson50_example9_moved")
shutil.move(str(backup_dir), str(moved_dir)) # переименование/перемещение папки
print(backup_dir.exists(), moved_dir.exists())
# False True
shutil.rmtree(source_dir) # рекурсивное удаление, даже если папка не пуста
shutil.rmtree(moved_dir)
shutil не входит в исходную лекцию (там разбирались только os и sys) — это дополнение курса. Полный список функций и их поведение при существующем целевом пути сверяйте с docs.python.org: shutil.
Что происходит: shutil.copytree() копирует папку рекурсивно вместе со всеми вложенными файлами и подпапками, shutil.move() переименовывает/переносит файл или папку (работает и между разными дисками, в отличие от os.rename()), а shutil.rmtree() — это то самое рекурсивное удаление, которого не хватает os.rmdir().
Пример 10. Скрипт для командной строки: sys.argv + pathlib
Финальный пример собирает вместе разбор пути и работу с командной строкой: скрипт принимает директорию аргументом и печатает её содержимое — то же, что Задание 3 из LMS, только на pathlib.
import sys
from pathlib import Path
# В реальном запуске аргумент приходит от ОС: python check_directory.py mydir
# Здесь подставляем его вручную, чтобы пример можно было выполнить сразу:
sys.argv = ["check_directory.py", "lesson50_example10_dir"]
cli_dir = Path("lesson50_example10_dir")
cli_dir.mkdir()
(cli_dir / "report.txt").write_text("data", encoding="utf-8")
if len(sys.argv) != 2:
print("Использование: python check_directory.py <директория>")
else:
target = Path(sys.argv[1])
if not target.is_dir():
print(f"Директория '{target}' не найдена.")
else:
print(f"Содержимое '{target}':")
for item in sorted(target.iterdir()):
label = "папка" if item.is_dir() else "файл"
print(f" {label}: {item.name}")
# Содержимое 'lesson50_example10_dir':
# файл: report.txt
(cli_dir / "report.txt").unlink()
cli_dir.rmdir()
Что происходит: sys.argv[0] — всегда имя скрипта (Ошибка 1), реальные аргументы начинаются с индекса 1. В настоящем скрипте вместо строки sys.argv = [...] аргумент передаёт пользователь при запуске: python check_directory.py lesson50_example10_dir, а при неверном количестве аргументов вместо print в ветке if обычно стоит ещё и sys.exit(1), как в справочнике — здесь он опущен, чтобы пример оставался самодостаточным и его можно было запустить целиком вместе с остальными.
Что дальше: исходники репозитория показывают те же идеи в формате лекции, задания — практика с os, а чтение самого содержимого файлов (open(), построчное чтение, контекстный менеджер with) разбирает следующий урок — 52. Основы работы с файлами.