Пишешь ли ты код вне Jupyter Notebook?

Ответ

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

Мой стандартный workflow:

  1. IDE: Использую VS Code с плагинами (Python, Pylance, Black) или PyCharm.
  2. Структура проекта: Создаю модульную структуру с самого начала.
    ml_project/
    ├── src/
    │   ├── __init__.py
    │   ├── data/
    │   │   ├── __init__.py
    │   │   └── make_dataset.py
    │   └── models/
    │       ├── __init__.py
    │       └── predict_model.py
    ├── tests/
    │   └── test_predict_model.py
    ├── notebooks/          # Только для EDA и прототипирования
    ├── pyproject.toml     # Зависимости через Poetry
    ├── Dockerfile
    └── README.md
  3. Инструменты:
    • Poetry для управления зависимостями и виртуальными окружениями.
    • Pre-commit hooks с black, isort, flake8 для автоматического форматирования и линтинга.
    • Pytest для модульного и интеграционного тестирования.
    • Git с осмысленными коммитами и Pull Request.

Пример модуля с аннотацией типов:

from typing import Optional
import pandas as pd
from sklearn.pipeline import Pipeline

def load_and_validate_data(filepath: str) -> pd.DataFrame:
    """Загружает CSV и проверяет базовую целостность."""
    df = pd.read_csv(filepath)
    if df.empty:
        raise ValueError(f"Файл {filepath} пуст.")
    # Проверка обязательных колонок
    required_cols = {"feature_1", "feature_2", "target"}
    if not required_cols.issubset(df.columns):
        missing = required_cols - set(df.columns)
        raise ValueError(f"Отсутствуют колонки: {missing}")
    return df

Такой подход позволяет легко интегрировать код в CI/CD пайплайны (например, GitHub Actions) и развертывать его.

Ответ 18+ 🔞

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

Мой стандартный подход, чтоб не было пиздопроебибны:

  1. IDE: Беру VS Code с кучей плагинов или PyCharm, если проект реально большой. Без этого — терпения ноль, ебать, глаза сломаешь.
  2. Структура проекта: Сразу делаю нормальную структуру, а не свалку файлов на рабочем столе. Вот пример, как должно выглядеть, чтобы не было стыдно:
    ml_project/
    ├── src/
    │   ├── __init__.py
    │   ├── data/
    │   │   ├── __init__.py
    │   │   └── make_dataset.py   # Тут всякая загрузка и первичная обработка
    │   └── models/
    │       ├── __init__.py
    │       └── predict_model.py  # А тут уже магия с обучением и предсказаниями
    ├── tests/                    # Без тестов — доверия ебать ноль
    │   └── test_predict_model.py
    ├── notebooks/                # Сюда складываю только черновики и разведку
    ├── pyproject.toml           # Зависимости через Poetry — красота!
    ├── Dockerfile               # На всякий пожарный
    └── README.md                # Чтобы коллеги не гадали, как это запустить
  3. Инструменты:
    • Poetry — чтобы не было ебанько с версиями библиотек. Виртуальное окружение, чёткие зависимости — красота.
    • Pre-commit hooks с black, isort, flake8. Автоматически форматирует код, как только пытаешься сделать коммит. Спасает от споров о стиле и откровенного говнокода.
    • Pytest — тесты, блядь. Без них чувствуешь себя, как на тонком льду. Написал функцию — сразу пару тестов к ней.
    • Git — с нормальными коммитами, а не «чё-то пофиксил». И пулл-реквесты, чтобы код кто-то ещё глянул.

Вот смотри, как я обычно модуль пишу, с аннотациями типов, чтобы было понятно, что куда летит:

from typing import Optional
import pandas as pd
from sklearn.pipeline import Pipeline

def load_and_validate_data(filepath: str) -> pd.DataFrame:
    """Загружает CSV и проверяет базовую целостность."""
    df = pd.read_csv(filepath)
    if df.empty:
        raise ValueError(f"Файл {filepath} пуст.")
    # Проверка обязательных колонок
    required_cols = {"feature_1", "feature_2", "target"}
    if not required_cols.issubset(df.columns):
        missing = required_cols - set(df.columns)
        raise ValueError(f"Отсутствуют колонки: {missing}")
    return df

Когда всё так структурировано, его хоть в CI/CD пайплайн (типа GitHub Actions) засовывай, хоть в докер-контейнер пакуй — никаких проблем. А главное, через полгода открываешь проект и сразу понимаешь, что к чему, а не тратишь день на то, чтобы вспомнить, какого хуя этот скрипт вообще работает.