1. Введение: Почему os.path — это прошлое, а pathlib — настоящее и будущее

Если вы работаете с файлами в Python, ваша мышечная память, скорее всего, заставляет вас писать import os. Десятилетиями os.path был нашим верным, хотя и неуклюжим, инструментом. Мы привыкли к манипуляциям со строками, бесконечным os.path.join() и необходимости постоянно помнить о различиях между / и \. Для полноценной работы приходилось импортировать целый набор модулей: os, glob, shutil. Этот подход родом из прошлого, и он давно требует модернизации.

Именно здесь на сцену выходит pathlib. Доступный с Python 3.4, этот модуль предлагает фундаментально иной, объектно-ориентированный подход. Вместо безликих строк мы получаем мощные объекты Path, которые инкапсулируют в себе не только сам путь, но и все операции с ним.

Забудьте о ручном склеивании строк: с pathlib пути элегантно конструируются с помощью оператора /. Проверка существования, чтение, получение родительской директории — всё это становится методами и атрибутами самого объекта. В результате код получается не просто чище и читабельнее, он становится более надежным и по-настоящему "питоничным" (Pythonic).

Эта статья — ваш гид по переходу от старых привычек к современному и эффективному коду. Мы наглядно покажем, почему пришло время оставить os.path в прошлом и в полной мере использовать мощь и элегантность pathlib. Ваш код скажет вам спасибо.

2. Первые шаги с pathlib: Пути как объекты

Главная идея pathlib проста и элегантна: путь в файловой системе — это не строка, а полноценный объект со своими атрибутами и методами. Это фундаментальное отличие открывает совершенно новый уровень удобства и контроля. Давайте посмотрим, как это работает на практике.

Создание объекта Path

Для начала работы достаточно импортировать один класс — Path — из модуля pathlib.

from pathlib import Path

Теперь мы можем создавать объекты путей из обычных строк. Path автоматически определит, с какой операционной системой вы работаете, и будет использовать правильный формат слешей.

# Создаем путь к файлу
config_path = Path('settings/production/database.ini')

# Получаем специальные пути
current_dir = Path.cwd()  # Current Working Directory (текущая рабочая директория)
home_dir = Path.home()    # Домашняя директория пользователя

print(f"Путь к конфигу: {config_path}")
print(f"Текущая директория: {current_dir}")
print(f"Домашняя директория: {home_dir}")

Магия оператора /: Забудьте про os.path.join()

Одно из самых заметных и приятных нововведений — возможность конструировать пути с помощью оператора деления /. Это не только выглядит чище, но и делает код более интуитивным.

Старый способ (os.path.join):

import os

root_path = '/etc'
subfolder = 'nginx'
filename = 'nginx.conf'

full_path = os.path.join(root_path, subfolder, filename)
print(full_path) # /etc/nginx/nginx.conf

Новый, элегантный способ (pathlib):

from pathlib import Path

root_path = Path('/etc')
# Просто "делим" путь на его компоненты
full_path = root_path / 'nginx' / 'nginx.conf'

print(full_path) # /etc/nginx/nginx.conf

Результат идентичен, но версия с pathlib читается как естественное предложение и не требует вложенных вызовов функций.

Абсолютные и относительные пути

Объекты Path "знают" о себе, являются ли они абсолютными (начинаются от корня файловой системы) или относительными.

path = Path('scripts/main.py')
print(f"Путь '{path}' абсолютный? {path.is_absolute()}") # False

# .resolve() превращает путь в абсолютный, разрешая все символические ссылки
absolute_path = path.resolve()
print(f"Абсолютный путь: {absolute_path}")
print(f"Путь '{absolute_path}' абсолютный? {absolute_path.is_absolute()}") # True

Разбор пути на компоненты

Когда у вас есть объект Path, получить любую его часть — имя файла, расширение или родительскую директорию — можно через простые атрибуты. Больше не нужно "пилить" строки вручную.

Рассмотрим путь path = Path('/home/user/project/main.py'):

path = Path('/home/user/project/main.py')

print(f"Родительская директория: {path.parent}")   # /home/user/project
print(f"Имя файла: {path.name}")                   # main.py
print(f"Имя без расширения: {path.stem}")          # main
print(f"Расширение: {path.suffix}")                # .py
print(f"Кортеж из частей пути: {path.parts}")      # ('/', 'home', 'user', 'project', 'main.py')

Атрибут .parents — это особый итератор, который позволяет "подниматься" вверх по дереву каталогов:

# path.parents[0] это то же самое, что и path.parent
print(f"Первый родитель: {path.parents[0]}") # /home/user/project
print(f"Второй родитель: {path.parents[1]}")  # /home/user

Как видите, уже на первых шагах pathlib избавляет нас от рутинных операций, предоставляя удобный и объектно-ориентированный интерфейс. В следующем разделе мы сравним его лицом к лицу с классическими подходами из модулей os и shutil.

3. pathlib против os и shutil: Битва титанов (с примерами кода)

Если предыдущий раздел не убедил вас в элегантности pathlib, то прямое сравнение с классическими инструментами расставит все по своим местам. Здесь мы наглядно, строка за строкой, покажем, насколько проще и интуитивнее становится код. Каждый пример будет содержать два блока: привычный нам способ с использованием os и shutil и современный, лаконичный подход с pathlib.

Проверка существования файла или директории

Одна из самых частых задач. С pathlib проверка становится методом самого объекта, что логичнее, чем передавать строку в отдельную функцию.

Старый способ (os.path):

import os

path_str = 'data/file.txt'

if os.path.exists(path_str):
    if os.path.isfile(path_str):
        print(f"Файл '{path_str}' существует.")
    elif os.path.isdir(path_str):
        print(f"Директория '{path_str}' существует.")

Новый способ (pathlib):

from pathlib import Path

path_obj = Path('data/file.txt')

if path_obj.exists():
    if path_obj.is_file():
        print(f"Файл '{path_obj}' существует.")
    elif path_obj.is_dir():
        print(f"Директория '{path_obj}' существует.")

Преимущество: Меньше импортов, более естественный синтаксис (объект.действие()).

Создание директорий

С модулем os нам приходилось использовать две разные функции: mkdir() для одной директории и makedirs() для создания всего дерева каталогов. pathlib объединяет эту логику в одном методе с удобными флагами.

Старый способ (os):

import os

path_str = 'archive/2025/reports'

# Приходится использовать try-except или предварительную проверку
# os.makedirs() с exist_ok=True — лучший вариант в старом подходе
try:
    os.makedirs(path_str)
    print(f"Директория '{path_str}' создана.")
except FileExistsError:
    print(f"Директория '{path_str}' уже существует.")

Новый способ (pathlib):

from pathlib import Path

path_obj = Path('archive/2025/reports')

# parents=True — создать родительские директории, если их нет
# exist_ok=True — не вызывать ошибку, если директория уже существует
path_obj.mkdir(parents=True, exist_ok=True)
print(f"Директория '{path_obj}' успешно создана или уже существует.")

Преимущество: Один универсальный метод mkdir() заменяет os.mkdir() и os.makedirs(), а его флаги делают код чище и избавляют от блоков try-except.

Чтение и запись файлов

Вот где pathlib сияет по-настоящему. Для простых операций чтения/записи текста или байтов больше не нужен менеджер контекста with open(...).

Старый способ (open):

import os

path_str = 'hello.txt'
content = 'Привет, мир pathlib!'

# Запись в файл
with open(path_str, 'w', encoding='utf-8') as f:
    f.write(content)

# Чтение из файла
with open(path_str, 'r', encoding='utf-8') as f:
    read_content = f.read()

print(read_content)

Новый способ (pathlib):

from pathlib import Path

path_obj = Path('hello.txt')
content = 'Привет, мир pathlib!'

# Запись в файл в одну строку!
path_obj.write_text(content, encoding='utf-8')

# Чтение из файла в одну строку!
read_content = path_obj.read_text(encoding='utf-8')

print(read_content)

Преимущество: Кардинальное сокращение шаблонного кода. Для бинарных файлов существуют аналогичные методы read_bytes() и write_bytes().

Переименование и перемещение

Операция перемещения или переименования также становится методом объекта, что делает код более объектно-ориентированным.

Старый способ (os):

import os

old_name = 'main.log'
new_name = 'main.log.old'

# Убедимся, что файл существует
if os.path.exists(old_name):
    os.rename(old_name, new_name)
    print(f"Файл '{old_name}' переименован в '{new_name}'.")

Новый способ (pathlib):

from pathlib import Path

src = Path('main.log')
dst = Path('main.log.old')

# Метод rename можно вызвать прямо на объекте
if src.exists():
    src.rename(dst)
    print(f"Файл '{src}' переименован в '{dst}'.")

Преимущество: Логика "что сделать с чем" (source.rename(destination)) выглядит более естественно, чем "сделать что-то с чем-то" (os.rename(source, destination)).

Удаление файлов и директорий

В старом подходе для удаления файла и пустой директории использовались разные функции из разных модулей. pathlib предлагает более последовательные имена.

Старый способ (os):

import os

file_to_del = 'temp.tmp'
dir_to_del = 'temp_dir'

if os.path.isfile(file_to_del):
    os.remove(file_to_del) # Для файлов используем os.remove()

if os.path.isdir(dir_to_del):
    os.rmdir(dir_to_del)   # Для пустых директорий — os.rmdir()

Новый способ (pathlib):

from pathlib import Path

file_to_del = Path('temp.tmp')
dir_to_del = Path('temp_dir')

# .unlink() для файлов (и символических ссылок)
if file_to_del.exists():
    file_to_del.unlink()

# .rmdir() для пустых директорий
if dir_to_del.exists():
    dir_to_del.rmdir()

Преимущество: Методы unlink() и rmdir() являются частью одного и того же интерфейса объекта Path. Стоит отметить, что для рекурсивного удаления непустой директории по-прежнему стоит использовать shutil.rmtree(), который, впрочем, прекрасно принимает в качестве аргумента объект Path.

4. Продвинутые возможности pathlib: Автоматизация и элегантные решения

Мы уже убедились, что pathlib — это отличная замена os.path. Но его настоящая сила раскрывается в задачах, требующих большего, чем просто манипуляции с путями. pathlib предоставляет элегантные инструменты для автоматизации, которые раньше требовали комбинации нескольких модулей и написания менее очевидного кода.

Поиск файлов с помощью glob и rglob

Вам нужно найти все .py файлы в проекте? Или все .jpg изображения в папке с фотографиями? Для этого больше не нужен отдельный модуль glob. Методы для поиска по шаблону встроены прямо в объекты Path.

  • .glob(pattern) — ищет файлы по шаблону в текущей директории.

  • .rglob(pattern) — ищет файлы рекурсивно (r от "recursive") во всех поддиректориях.

Старый способ (glob):

import glob
import os

# Нужно вручную конструировать путь для поиска
search_pattern = os.path.join('src', '**', '*.py')

# recursive=True необходимо для работы '**'
py_files = glob.glob(search_pattern, recursive=True)

for file_path_str in py_files:
    # Мы получаем строки, которые для дальнейшей работы нужно снова превращать в объекты
    print(file_path_str)

Новый способ (pathlib):

from pathlib import Path

project_dir = Path('src')

# rglob возвращает генератор, что экономит память
py_files_generator = project_dir.rglob('*.py')

for path_obj in py_files_generator:
    # Мы сразу получаем Path объекты, готовые к работе!
    print(f"Найден файл: {path_obj}, размер: {path_obj.stat().st_size} байт")

Преимущество: Код становится проще, а результат — удобнее. Вместо списка строк мы получаем генератор полноценных объектов Path, с которыми можно сразу продолжить работу, например, узнать их размер, как в примере выше.

Итерация по содержимому директории

Получить список всего, что лежит в папке, — базовая задача. pathlib и здесь предлагает более "питоничный" путь.

Старый способ (os.listdir):

import os

dir_path = 'src'
for item_name in os.listdir(dir_path):
    # os.listdir() возвращает только имена, а не полные пути
    full_path_str = os.path.join(dir_path, item_name)
    if os.path.isfile(full_path_str):
        print(f"Файл: {full_path_str}")

Новый способ (pathlib.iterdir):

from pathlib import Path

dir_path = Path('src')

# iterdir() возвращает генератор объектов Path
for item_path in dir_path.iterdir():
    # Мы сразу работаем с полноценными путями
    if item_path.is_file():
        print(f"Файл: {item_path}")

Преимущество: iterdir() избавляет от необходимости вручную склеивать путь к директории с именем файла. Как и glob, он возвращает генератор, что эффективно при работе с очень большими директориями.

Работа с метаданными файлов

Получить размер файла, дату создания или последней модификации теперь можно одним вызовом метода .stat().

Пример: Поиск 10 самых больших файлов в директории

Это отличный пример, объединяющий несколько продвинутых техник:

from pathlib import Path

# Директория для поиска (например, домашняя)
search_dir = Path.home()

# 1. Используем rglob для рекурсивного поиска всех файлов
# 2. Фильтруем, оставляя только файлы (на случай, если попадется ссылка на директорию)
# 3. Создаем список кортежей (размер_файла, путь_к_файлу)
all_files = [
    (p.stat().st_size, p)
    for p in search_dir.rglob('*')
    if p.is_file()
]

# 4. Сортируем список по убыванию размера и берем первые 10
all_files.sort(key=lambda x: x[0], reverse=True)

print("Топ-10 самых больших файлов:")
for size, path in all_files[:10]:
    # Приводим размер к мегабайтам для читаемости
    print(f"  {path.name:<40} | {size / 1024 / 1024:.2f} MB")

Этот компактный и читаемый скрипт демонстрирует всю мощь pathlib в задачах автоматизации. Попробуйте написать то же самое с использованием модуля os — кода будет значительно больше.

"Чистые пути" (PurePath): Манипуляции без доступа к ФС

Иногда нужно работать с путями для другой операционной системы. Например, на Linux-сервере сгенерировать путь для Windows-машины. Обычный Path будет пытаться взаимодействовать с файловой системой, что вызовет ошибку.

Для таких задач существуют "чистые" классы: PurePath, PurePosixPath и PureWindowsPath. Они обладают всеми методами для манипуляции путями (/, .parent, .name), но не пытаются обращаться к файловой системе.

from pathlib import PureWindowsPath

# Этот код отлично сработает на любой ОС: Linux, macOS или Windows
win_path = PureWindowsPath('C:/Users/Admin') / 'Documents' / 'report.docx'

print(f"Сгенерированный путь Windows: {win_path}") # Вывод: C:\Users\Admin\Documents\report.docx
print(f"Имя файла: {win_path.name}")
print(f"Родитель: {win_path.parent}")

Преимущество: Это незаменимый инструмент для написания кросс-платформенных утилит, систем сборки или генераторов конфигураций.

5. Практические кейсы: Где pathlib особенно хорош

Теория — это хорошо, но истинная ценность инструмента проверяется в бою. pathlib блистает в повседневных задачах автоматизации и в структурировании больших проектов. Давайте рассмотрим несколько реалистичных сценариев, где его применение дает максимальный эффект.

Кейс 1: Скрипт для организации файлов в "Загрузках"

У каждого из нас есть папка "Загрузки" — хаотичное скопление документов, изображений, архивов и инсталляторов. Напишем простой скрипт, который будет раскладывать файлы по папкам в зависимости от их расширения.

Задача: Переместить все .jpg и .png в папку Images, .pdf и .docx — в Documents, .zip и .rar — в Archives, а все остальное оставить на месте.

from pathlib import Path
import shutil

# 1. Определяем директорию для работы
source_dir = Path.home() / 'Downloads'

# 2. Определяем "карту" распределения: расширение -> папка
DEST_MAP = {
    ".jpg": "Images",
    ".jpeg": "Images",
    ".png": "Images",
    ".gif": "Images",
    ".pdf": "Documents",
    ".docx": "Documents",
    ".txt": "Documents",
    ".zip": "Archives",
    ".rar": "Archives",
}

# 3. Итерируемся по всем файлам в директории
for path in source_dir.iterdir():
    # Пропускаем директории и обрабатываем только файлы
    if path.is_dir():
        continue

    # 4. Если расширение файла есть в нашей карте
    if path.suffix in DEST_MAP:
        # Определяем папку назначения
        dest_dir_name = DEST_MAP[path.suffix]
        dest_dir = source_dir / dest_dir_name

        # 5. Создаем папку назначения, если ее нет
        dest_dir.mkdir(exist_ok=True)

        # 6. Перемещаем файл
        # Для перемещения между разными дисками лучше использовать shutil.move
        # Но для простого перемещения path.rename() идеален
        print(f"Перемещение {path.name} в {dest_dir_name}...")
        path.rename(dest_dir / path.name)

Почему здесь pathlib идеален?

  • .home() / 'Downloads': Интуитивное построение пути.

  • .iterdir(): Простая и эффективная итерация.

  • .is_dir(): Легкая проверка типа объекта.

  • .suffix: Простое получение расширения без строковых операций.

  • .mkdir(exist_ok=True): Идемпотентное создание директорий в одну строку.

  • .rename(): Лаконичное перемещение файла.

Кейс 2: Надежная настройка путей в проектах

Это, пожалуй, одно из самых важных применений pathlib. В любом серьезном проекте (веб-приложение на Django/Flask, data science пайплайн) нельзя жестко прописывать относительные пути вроде ../data/raw.csv. Такой код сломается, если вы запустите скрипт из другой директории.

Задача: Получить абсолютный путь к корню проекта, независимо от того, откуда запущен скрипт, и на его основе строить пути к другим ресурсам (шаблонам, данным, статике).

Решение — "золотой стандарт" с pathlib:

Предположим, у нас такая структура проекта:

my_project/
├── data/
│   └── raw.csv
├── notebooks/
│   └── analysis.ipynb
└── src/
    ├── __init__.py
    └── settings.py

В файле src/settings.py мы хотим определить базовую директорию проекта.

# src/settings.py
from pathlib import Path

# __file__ — это строка с путем к текущему файлу (src/settings.py)
# Path(__file__) — создаем из нее объект Path
# .resolve() — получаем полный абсолютный путь к файлу, разрешая все символические ссылки
# .parent — получаем родительскую директорию (папку 'src')
# .parent — еще раз "поднимаемся" вверх и получаем корень проекта ('my_project')
BASE_DIR = Path(__file__).resolve().parent.parent

# Теперь можно безопасно строить пути к любым ресурсам в проекте
DATA_DIR = BASE_DIR / 'data'
RAW_DATA_PATH = DATA_DIR / 'raw.csv'

print(f"Корень проекта: {BASE_DIR}")
print(f"Путь к данным: {RAW_DATA_PATH}")
print(f"Существует ли файл с данными? {RAW_DATA_PATH.exists()}")

Почему это так важно?

  • Надежность: Этот код будет работать всегда, независимо от того, запустите ли вы его из my_project/, my_project/src/ или вообще из /tmp.

  • Читаемость: Конструкция BASE_DIR / 'data' гораздо понятнее, чем os.path.join(os.path.dirname(os.path.dirname(__file__)), 'data').

  • Переносимость: Код без проблем заработает на Windows, Linux и macOS.

Этот паттерн настолько распространен и полезен, что его можно встретить практически в любом современном Python-фреймворке. Использование pathlib здесь — признак профессионального и качественного кода.

6. Производительность и возможные "подводные камни"

Несмотря на все свои преимущества, ни один инструмент не является идеальным решением для абсолютно всех задач. При переходе на pathlib стоит знать о паре нюансов, касающихся производительности и взаимодействия с другим кодом.

Вопрос производительности: стоит ли беспокоиться?

Самый частый вопрос от опытных разработчиков: "Не медленнее ли pathlib, ведь он создает объекты вместо работы с простыми строками?"

Короткий ответ: Да, pathlib незначительно медленнее, но в 99.9% случаев это абсолютно не имеет значения.

Развернутый ответ:
Создание объекта Path действительно несет небольшие накладные расходы по сравнению с манипуляцией строкой. Если вы в очень тесном цикле будете миллионы раз выполнять чисто строковые операции с путями, не обращаясь к диску (например, просто конструировать пути в памяти), то os.path.join() на чистых строках покажет себя немного быстрее.

Однако в реальном мире узким местом практически всегда являются операции ввода-вывода (I/O) — чтение с диска, запись на диск, проверка существования файла. Эти операции на порядки медленнее, чем создание любого Python-объекта. На фоне времени, которое уходит на обращение к файловой системе, разница в скорости между pathlib и os.path становится совершенно незаметной.

Вывод: Для подавляющего большинства приложений — от веб-сервисов до скриптов автоматизации и анализа данных — выигрыш в читаемости, надежности и скорости разработки, который дает pathlib, многократно перевешивает микроскопический проигрыш в производительности. Не занимайтесь преждевременной оптимизацией. Выбирайте pathlib по умолчанию и думайте о производительности только в том случае, если профилирование кода явно укажет на это место как на узкое горлышко.

Подводный камень №1: Взаимодействие со старыми библиотеками

pathlib появился в Python 3.4. Многие старые библиотеки или даже встроенные функции, написанные до этого, ожидают на вход исключительно строку (str), а не объект Path. Если передать в такую функцию Path, вы можете получить TypeError.

Проблема:

import some_legacy_library
from pathlib import Path

# Библиотека ожидает строку, а мы передаем объект
config_path = Path('configs/main.ini')
# some_legacy_library.load_config(config_path) # -> TypeError!

Решение:
К счастью, оно очень простое — явно преобразовать объект Path в строку с помощью str().

# Правильное решение:
some_legacy_library.load_config(str(config_path))

Стоит отметить, что начиная с Python 3.6, большинство встроенных функций и современных библиотек научились работать с "path-like objects" (объектами, подобными путям), и такая конвертация требуется все реже. Но эту особенность полезно держать в уме при работе с унаследованным кодом.

Подводный камень №2: pathlib — это не замена всего os и shutil

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

Например, в pathlib нет встроенных аналогов для:

  • shutil.copytree(): рекурсивного копирования дерева каталогов.

  • shutil.rmtree(): рекурсивного удаления непустой директории.

  • os.chmod(): изменения прав доступа к файлу.

  • os.chown(): изменения владельца файла.

Хорошая новость: вам и не нужно отказываться от этих функций. Все они прекрасно работают с объектами Path, принимая их в качестве аргументов.

import shutil
from pathlib import Path

source_dir = Path('project_backup')
destination_dir = Path('project_new')

# pathlib и shutil отлично работают вместе!
if source_dir.exists():
    shutil.rmtree(destination_dir, ignore_errors=True) # shutil.rmtree принимает Path
    shutil.copytree(source_dir, destination_dir)      # и shutil.copytree тоже
    print("Проект успешно скопирован!")

Таким образом, pathlib не вытесняет os и shutil полностью, а гармонично дополняет их, беря на себя всю работу с представлением и базовыми манипуляциями путей.

7. Заключение: Время действовать

Итак, мы прошли путь от неуклюжих строковых манипуляций os.path до объектно-ориентированной элегантности pathlib. Преимущества очевидны: код становится чище, надежнее и интуитивно понятнее. pathlib — это не просто синтаксический сахар, а фундаментальное улучшение, объединяющее лучшее из os, shutil и glob в едином, последовательном интерфейсе.

Практические задачи для закрепления

Чтобы лучше усвоить материал, попробуйте решить эти небольшие задачи, используя только pathlib.

Задача 1: Разбор пути

Создайте объект Path для воображаемого пути 'project/src/utils/helpers.py'. Выведите на экран три строки:

  1. Родительскую директорию этого файла.

  2. Имя файла с расширением.

  3. Только расширение файла.

Задача 2: Создание и чтение файла

Напишите скрипт, который создает в текущей директории файл с именем info.txt и записывает в него строку "Pathlib is awesome!". Сразу после этого скрипт должен прочитать содержимое этого файла и вывести его в консоль. Все операции (запись и чтение) должны быть выполнены в одну строку каждая.

Задача 3: Создание дерева директорий

Напишите код, который создает в текущей директории вложенную структуру папок: data/raw/logs. Ваше решение должно работать в одну команду и не вызывать ошибку, если какая-либо из этих директорий уже существует.

Задача 4: Поиск файлов

Представьте, что в вашей текущей директории есть файлы main.py, test_main.py, config.json и README.md. Напишите скрипт, который найдет и выведет имена всех файлов с расширением .py, находящихся только в этой директории (без рекурсивного поиска).

Задача 5: Пакетное переименование

Сначала создайте несколько пустых файлов для теста: report-2025-01.txt, report-2025-02.txt и image.jpg. Напишите скрипт, который найдет все файлы, начинающиеся со слова report-, и добавит к их имени суффикс .old. В результате файлы должны быть переименованы в report-2025-01.txt.old и report-2025-02.txt.old.

Анонс новых статей, полезные материалы, а так же если в процессе решения возникнут сложности, обсудить их или задать вопрос по статье можно в моём Telegram-сообществе.

Уверен, у вас все получится. Вперед, к практике!

Комментарии (2)


  1. wl2776
    27.10.2025 08:49

    Надежная настройка путей в проектах

    FYI, есть готовый модуль rootutils, написанный поверх pathlib, который это делает одной функцией.


  1. Vindicar
    27.10.2025 08:49

    В пункте 3 контент повторяется дважды.