$ sudo teach IT
МОДУЛЬ 9 · УРОК 1

Разбивка кода на файлы — зачем и как

Вы написали 2000 строк в одном файле main.py. Поздравляем — вы только что создали себе головную боль на годы вперёд. В этом уроке разберёмся, почему один большой файл — это проблема, и как правильно организовать код по файлам.

⏱ ~35 минут 🎓 Для новичков 🐍 Python 3

😰 Представьте: 2000 строк в одном файле

Когда вы только начинаете программировать, всё кажется просто: создал файл main.py, написал туда весь код, запустил — работает. Отлично!

Но проект растёт. Появляются новые классы, функции, константы. Файл набухает: сначала 200 строк, потом 500, потом 1000, 2000... И вот здесь начинается настоящий кошмар.

📖 Аналогия: книга без глав

Представьте книгу на 600 страниц — без оглавления, без глав, без разделов. Просто сплошной текст от начала до конца. Вы хотите найти место, где говорится про главного героя во второй части. Удачи вам в поиске! Именно так ощущается работа с «монолитным» файлом кода.

🏠 Аналогия: квартира без комнат

Или представьте квартиру — огромный зал без стен и перегородок. В одном углу спят, в другом готовят, в третьем работают, посередине стоит диван, рядом — холодильник, стиральная машина и письменный стол. Всё в одном пространстве. Неудобно? Так же неудобно работать с кодом, где классы, функции, конфигурация и тесты свалены в один файл.

Разберём конкретные боли, которые появляются с ростом файла:

🔍

Поиск нужного места

«Где у меня класс User? На какой строке функция calculate_price?» Ctrl+F по файлу на 2000 строк — не самое приятное занятие.

🤝

Работа в команде

Вы оба с коллегой работаете над main.py. Каждый раз при слиянии (merge) в git — конфликты. Один файл не может редактировать два человека без столкновений.

😵

Понимание кода

Через три месяца вы открываете свой же файл и не помните, что там написано. Когда всё смешано — очень сложно удержать в голове, что к чему относится.

♻️

Переиспользование

Вы написали отличную функцию для другого проекта. Но забрать её нельзя — она зарыта среди 2000 строк и зависит от всего остального кода вокруг.

🚨 Главная боль монолитного файла — это не то, что компьютеру тяжело его выполнять. Компьютеру всё равно. Боль в том, что с таким кодом мучаются люди: вы сами через месяц, ваши коллеги, все кто будет поддерживать этот код.

💡 Главная мысль: разбивка кода — это про людей

Прежде чем идти дальше, запомните одну фундаментальную вещь:

Код читают люди.
Компьютер выполняет байт-код — ему всё равно, как вы назвали файлы и разложили классы.

Когда вы разбиваете проект на файлы — вы создаёте структуру для людей. Каждый файл — это отдельная «тема», отдельная «глава книги». Открыв файл models/user.py, любой разработчик мгновенно понимает: здесь живёт всё, что связано с пользователями.

Хорошо структурированный проект — это проявление уважения к другим людям (и к себе будущему). Это профессиональный навык, который отличает джуниора от мидла.

🌱 Принцип единственной ответственности (Single Responsibility): каждый файл, каждый класс, каждая функция должны делать одну вещь и делать её хорошо. Один файл — одна тема. Это делает код понятным, тестируемым и переиспользуемым.

📁 До и после: структура проекта

Рассмотрим конкретный пример. Допустим, вы пишете простой интернет-магазин. Вот как выглядит типичный «монолитный» подход и правильная структура:

❌ До: всё в одном файле

shop/
└── main.py          ← 2347 строк
    # строки 1-50: импорты
    # строки 51-200: класс User
    # строки 201-400: класс Product
    # строки 401-500: класс Cart
    # строки 501-700: класс Order
    # строки 701-900: функции работы с БД
    # строки 901-1100: функции отправки email
    # строки 1101-1400: расчёт доставки
    # строки 1401-1700: API-эндпоинты
    # строки 1701-2000: утилиты и хелперы
    # строки 2001-2347: тесты (!!!)

✅ После: логическая структура

shop/
├── main.py          ← 30 строк (точка входа)
├── models/
│   ├── user.py      ← класс User
│   ├── product.py   ← класс Product
│   ├── cart.py      ← класс Cart
│   └── order.py     ← класс Order
├── services/
│   ├── database.py  ← работа с БД
│   ├── email.py     ← отправка писем
│   └── delivery.py  ← расчёт доставки
├── api/
│   └── endpoints.py ← API-эндпоинты
├── utils/
│   └── helpers.py   ← утилиты
└── tests/
    ├── test_user.py
    └── test_order.py

Видите разницу? Во втором случае, чтобы найти класс User, не нужно ничего искать — вы сразу открываете models/user.py. Хотите исправить расчёт доставки? services/delivery.py. Всё логично и предсказуемо.

🗂️ Принцип хорошей структуры: человек, видящий ваш проект впервые, должен за 10 секунд понять, где что лежит. Если структура требует объяснений — она недостаточно хороша.

🧩 Логическая группировка: каждый файл — одна тема

Как решить, что положить в какой файл? Задайте себе вопрос: «О чём этот код?»

Код, который отвечает на один и тот же вопрос, должен жить вместе. Несколько практических правил:

📦 Модели данных → папка models/

Классы, которые описывают ваши данные: User, Product, Order. Каждый класс — свой файл. Файл называется так же, как класс, только в нижнем регистре.

⚙️ Бизнес-логика → папка services/

Функции, которые «делают что-то»: считают цену, отправляют письма, обращаются к базе данных. Это сервисы — логика работы приложения.

🔧 Вспомогательные функции → папка utils/

Небольшие «инструменты», которые используются везде: форматирование даты, валидация email, генерация ID. Не относятся ни к одной конкретной теме, но нужны всем.

🧪 Тесты → папка tests/

Тесты всегда отдельно от основного кода! И структура папки tests/ обычно зеркалит структуру основного проекта: есть models/user.py — значит, есть и tests/test_user.py.

Вот как выглядит маленький учебный проект — магазин с тремя классами, разбитый по файлам:

my_shop/
├── main.py               ← запускаем отсюда
├── models/
│   ├── __init__.py       ← пустой файл, делает папку пакетом Python
│   ├── product.py        ← класс Product
│   └── cart.py           ← класс Cart
└── utils/
    ├── __init__.py
    └── formatters.py     ← вспомогательные функции

Заметили файлы __init__.py? Это специальные пустые файлы, которые говорят Python: «эта папка — пакет, из неё можно импортировать». Без них Python не увидит папку как место, откуда можно делать import. Подробнее разберём в следующем уроке.

👀 Читаемость: проще найти нужный класс

Представьте: продакт-менеджер говорит вам: «Нужно добавить скидку к продукту». Где вы будете делать изменения?

❌ Монолитный проект

Открываем main.py — 2000 строк. Ищем class Product... Ctrl+F, нашли на строке 247. Вносим изменение. А подождите, тут же рядом 50 строк, которые непонятно к чему относятся. Осторожно, не сломать бы что-то ещё...

✅ Правильная структура

Открываем models/product.py — 80 строк, и только class Product. Находим нужное место моментально, добавляем поле discount. В этом файле нет ничего лишнего, всё относится только к продукту. Спокойно, без страха.

Маленький файл с одной темой — это фокус внимания. Когда вы открываете models/product.py, вы точно знаете: всё, что здесь есть, связано с продуктом. Больше ничего. Это снижает когнитивную нагрузку и уменьшает риск ошибок.

🎯 Правило: если в файле больше 200–300 строк и он касается разных тем — это сигнал, что его пора разбить. Конечно, это не жёсткое правило, но хороший ориентир для начинающих.

👥 Командная разработка: разные люди — разные файлы

Программирование в реальных компаниях — это командная работа. Над одним проектом могут одновременно работать 5, 10, 50 человек. И здесь структура проекта становится критически важной.

Рассмотрим ситуацию с системой контроля версий git — инструментом, который отслеживает изменения в коде и помогает команде работать вместе:

❌ Проблема с одним файлом

Катя работает над классом User в main.py (строки 50–200). Коля одновременно работает над классом Product в том же main.py (строки 201–400). Оба сохраняют изменения в git.

Результат: конфликт при слиянии (merge conflict). Git не понимает, чьи изменения правильные, потому что оба изменили один и тот же файл. Придётся вручную разбираться, какие строки оставить — и это отнимает время, нервы и создаёт риск ошибок.

✅ Решение с правильной структурой

Катя работает в models/user.py. Коля работает в models/product.py. Это разные файлы — git без проблем сливает их изменения автоматически, без конфликтов. Оба могут работать параллельно, не мешая друг другу.

👨‍💻 На практике: в больших компаниях принято, что каждый разработчик «владеет» своей областью кода. Один отвечает за модели, другой — за API, третий — за тесты. Правильная структура файлов делает эту «границу ответственности» очевидной и чёткой.

♻️ Переиспользование: модуль в другом проекте

Ещё одно мощное преимущество хорошей структуры — возможность использовать код повторно.

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

❌ Монолитный main.py

Функции зарыты в середине 2000 строк. Чтобы их забрать, нужно аккуратно вырезать нужные части, проверить что они не зависят от другого кода вокруг... Скорее всего, они зависят — и придётся тянуть ещё половину файла.

✅ Файл utils/dates.py

Все функции для дат живут в одном файле и зависят только друг от друга. Просто скопируйте dates.py в новый проект и напишите from utils.dates import format_date. Готово!

Именно так работают популярные библиотеки Python — requests, pandas, numpy. Каждая из них — это набор хорошо организованных файлов с чёткими темами. Вы просто пишете import requests и получаете доступ к сотням функций.

🚀 Мечта разработчика: написал модуль один раз — используешь в десяти проектах. Это возможно только если код хорошо изолирован в отдельных файлах с чёткими зависимостями.

💻 Как это выглядит в коде

Давайте посмотрим на конкретный пример. Возьмём небольшой проект магазина и разобьём его по файлам. Всё, что нужно знать об import — разберём в следующем уроке, сейчас просто смотрите на структуру.

Файл models/product.py — только о продукте:

# models/product.py
# Этот файл знает только об одном: что такое Product

class Product:
    def __init__(self, name, price):
        self.name = name
        self.price = price

    def apply_discount(self, percent):
        """Применить скидку в процентах"""
        self.price = self.price * (1 - percent / 100)

    def __str__(self):
        return f"{self.name} — {self.price:.2f} руб."

Файл models/cart.py — только о корзине:

# models/cart.py
# Этот файл знает только об одном: что такое Cart

from models.product import Product  # берём Product из соседнего файла

class Cart:
    def __init__(self):
        self.items = []

    def add(self, product):
        self.items.append(product)

    def total(self):
        return sum(item.price for item in self.items)

    def __str__(self):
        lines = [str(item) for item in self.items]
        lines.append(f"Итого: {self.total():.2f} руб.")
        return "\n".join(lines)

Файл main.py — только точка входа, минимум кода:

# main.py
# Точка входа в программу — только запускает и соединяет части

from models.product import Product
from models.cart import Cart

# Создаём продукты
apple = Product("Яблоко", 50)
banana = Product("Банан", 30)
apple.apply_discount(10)   # скидка 10%

# Собираем корзину
cart = Cart()
cart.add(apple)
cart.add(banana)

print(cart)
# Яблоко — 45.00 руб.
# Банан — 30.00 руб.
# Итого: 75.00 руб.

✨ Обратите внимание: main.py стал чистым и коротким. Он просто «собирает» компоненты вместе. Каждый модуль делает своё дело. Хотите изменить логику корзины? Открываете models/cart.py — и только его.

📐 Принципы хорошей структуры проекта

Запомните несколько простых правил, которые помогут вам организовать код правильно:

1️⃣

Один файл — одна тема

Файл product.py содержит только код, относящийся к продуктам. Никаких «заодно положу сюда ещё функцию для отправки email».

2️⃣

Имена файлов должны говорить сами за себя

user.py, email_sender.py, price_calculator.py — из названия понятно содержимое. stuff.py, misc.py, utils2.py — плохие имена.

3️⃣

Группируйте по смыслу, а не по типу

Лучше папка orders/ со всем, что связано с заказами, чем папка classes/ со всеми классами проекта. Папки отражают области задач, не технические категории.

4️⃣

Не дробите слишком мелко

Файл на 10 строк с одной функцией — перебор. Если несколько связанных функций вместе занимают 50 строк, им хорошо в одном файле. Цель — читаемость, не максимальное количество файлов.

5️⃣

main.py — только точка входа

Файл main.py (или app.py) должен быть коротким. Его задача — запустить приложение, собрав части вместе. Вся логика — в модулях.

✅ Итоги урока

  • Монолитный файл на 2000 строк — это не проблема для компьютера, это проблема для людей: сложно искать, страшно менять, невозможно работать в команде без конфликтов.
  • Разбивка по файлам — это про читаемость, сопровождение и командную работу. Компьютеру всё равно, люди скажут спасибо.
  • Каждый файл — одна тема: user.py о пользователях, cart.py о корзине, email.py об отправке писем. Название файла = тема файла.
  • Git и команда: разные люди работают с разными файлами — нет конфликтов при слиянии, каждый знает свою область.
  • Переиспользование: изолированный модуль легко перенести в другой проект — просто скопируй файл и импортируй.
  • main.py должен быть коротким — только точка входа, которая собирает компоненты вместе.

👉 В следующем уроке мы разберём механику Python-модулей: что такое import, как Python ищет файлы, зачем нужен __init__.py, и как правильно импортировать классы из других файлов. Теория закончилась — начнём писать код с нуля в нескольких файлах.