Языки7 мин чтения2026-03-06

Качество кода Python: полный гайд от разработчика

Если ты пишешь код на Python, то наверняка сталкивался с ситуацией: вчера всё работало, сегодня тест упал, а послезавтра обнаружилась утечка памяти. Обидно, пра

Если ты пишешь код на Python, то наверняка сталкивался с ситуацией: вчера всё работало, сегодня тест упал, а послезавтра обнаружилась утечка памяти. Обидно, правда? Я встречал такое на каждом проекте — от стартапов до Яндекса.

Проблема не в самом Python. Язык хороший, гибкий, продуктивный. Беда в том, что его гибкость — это двойной меч. Ты можешь написать "работающий" код за 5 минут. Но будет ли это качественный код? Вот об этом мы и поговорим.

За 10 лет я видел сотни Python-проектов. Лучшие из них придерживались простых правил. Худшие? Ну, худшие были настоящим кошмаром. И разница была не в талантливости разработчиков, а в подходе.

Давай разберёмся, как писать Python-код, который не будет источником проблем через месяц.

Типизация — первый шаг к качеству

Вот честно? Большинство Python-разработчиков игнорируют type hints. Пишут код без них и думают, что это нормально. Потом приходит новый человек в команду и... ломает всё.

Type hints — это не просто формальность. Это твой договор с будущим тобой и с коллегами.

# Плохо: что вернёт функция? Кто знает
def calculate_discount(price, discount):
    return price * (1 - discount)

# Хорошо: сразу видно, что ожидается
def calculate_discount(price: float, discount: float) -> float:
    return price * (1 - discount)

Начинаешь добавлять type hints — и сразу видишь проблемы, которые раньше были скрыты. На одном проекте в Яндексе мы добавили полную типизацию к существующему коду. Нашли 47 потенциальных багов. Сорок семь! И мы считали, что код работал нормально.

Используй mypy для статической проверки типов:

pip install mypy
mypy your_module.py

Конфиг в mypy.ini:

[mypy]
python_version = 3.11
warn_return_any = True
warn_unused_configs = True
disallow_untyped_defs = True
disallow_incomplete_defs = True

Флаг disallow_untyped_defs заставит тебя типизировать всё. Болезненно? Да. Но потом спасает жизнь.

Структура проекта: что на самом деле работает

Случайная структура папок — это путь к хаосу. Я видел проекты, где code review превращался в детектив. Где файлы лежат как попало, и никто не знает, где что искать.

Вот что работает на практике:

project/
├── src/
│   └── myapp/
│       ├── __init__.py
│       ├── main.py
│       ├── models/
│       │   ├── __init__.py
│       │   ├── user.py
│       │   └── product.py
│       ├── services/
│       │   ├── __init__.py
│       │   └── user_service.py
│       └── utils/
│           ├── __init__.py
│           └── validators.py
├── tests/
│       ├── unit/
│       └── integration/
├── pyproject.toml
├── setup.py
├── requirements.txt
└── README.md

Почему src/ папка? Потому что при импортах ты всегда явно указываешь путь. Нет случайных импортов из текущей директории. Это спасает при установке пакета.

В pyproject.toml указываешь зависимости, версию Python, инструменты для анализа:

[build-system]
requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"

[project]
name = "myapp"
version = "0.1.0"
requires-python = ">=3.10"
dependencies = [
    "requests>=2.28.0",
    "pydantic>=2.0",
]

[project.optional-dependencies]
dev = [
    "pytest>=7.0",
    "mypy>=1.0",
    "black>=23.0",
    "ruff>=0.1.0",
]

[tool.black]
line-length = 100

[tool.ruff]
line-length = 100
select = ["E", "F", "W"]

[tool.mypy]
python_version = "3.10"
disallow_untyped_defs = true

Вот это уже выглядит как серьёзный проект.

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

Знаешь, что самое странное? Люди тратят часы на code review, вручную ищут проблемы, которые может найти бот за 2 секунды.

Вот что я использую и рекомендую:

Ruff — быстрый linter на Rust. Ловит синтаксис, стиль, потенциальные баги:

pip install ruff
ruff check src/
ruff format src/  # автоматическое форматирование

Pylint — более строгий анализ, может найти настоящие ошибки логики:

pip install pylint
pylint src/myapp/

Black — форматирование кода. Просто. Быстро. Без дискуссий:

pip install black
black src/

Конфиг для локального использования (.pre-commit-config.yaml):

repos:
  - repo: https://github.com/psf/black
    rev: 23.12.0
    hooks:
      - id: black
        language_version: python3.11

  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.1.8
    hooks:
      - id: ruff
        args: [--fix]
      - id: ruff-format

  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.5.0
    hooks:
      - id: trailing-whitespace
      - id: end-of-file-fixer
      - id: check-yaml
      - id: check-added-large-files

Установи pre-commit:

pip install pre-commit
pre-commit install

Теперь перед каждым коммитом автоматически запустятся проверки. Забыл добавить type hint? Не сформатировал код? Pre-commit не даст закоммитить.

Оптимизация Python кода: где реально теряется производительность

Многие думают, что Python медленный. Правда в том, что медленный может быть только твой код.

На одном проекте я встретил вот такое:

# Ужас. O(n²)
def find_duplicates(items):
    duplicates = []
    for i in range(len(items)):
        for j in range(i + 1, len(items)):
            if items[i] == items[j]:
                duplicates.append(items[i])
    return duplicates

Работает? Да. Но на 100k элементов это 10 миллиардов сравнений. Программист ждёт... ждёт... и тиканье кулера становится частью его жизни.

Вот правильно:

# O(n). Быстро и понятно
def find_duplicates(items):
    seen = set()
    duplicates = set()
    for item in items:
        if item in seen:
            duplicates.add(item)
        else:
            seen.add(item)
    return list(duplicates)

Другой типичный пример — работа со строками:

# Плохо: создание новой строки на каждой итерации
result = ""
for word in words:
    result += word + " "

# Хорошо: join работает в 1000 раз быстрее
result = " ".join(words)

Использование профайлера — это не опция, это необходимость:

import cProfile
import pstats

def your_function():
    # твой код
    pass

profiler = cProfile.Profile()
profiler.enable()

your_function()

profiler.disable()
stats = pstats.Stats(profiler)
stats.sort_stats('cumulative')
stats.print_stats(10)  # топ 10 функций по времени

Или проще — используй py-spy для профилирования уже работающего кода:

pip install py-spy
py-spy record -o profile.svg python your_script.py

Тестирование: без этого качество кода — иллюзия

Честно? Я редко вижу хорошо протестированный Python-код. А должно быть наоборот.

Pytest — это стандарт. Настолько простой и мощный, что нет оправданий:

# tests/test_calculator.py
import pytest
from src.myapp.calculator import calculate_discount

def test_calculate_discount():
    assert calculate_discount(100, 0.1) == 90.0

def test_calculate_discount_edge_case():
    assert calculate_discount(0, 0.5) == 0.0

def test_calculate_discount_invalid():
    with pytest.raises(ValueError):
        calculate_discount(-100, 0.1)

Запуск:

pytest tests/ -v --cov=src/myapp

Флаг --cov покажет покрытие кода тестами. Цель — минимум 80%. На критичных местах — 100%.

Вот конфиг для pyproject.toml:

[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = "test_*.py"
addopts = "--strict-markers -v --tb=short"
markers = [
    "unit: unit tests",
    "integration: integration tests",
]

И маркируй тесты:

@pytest.mark.unit
def test_calculate_discount():
    ...

@pytest.mark.integration
def test_api_integration():
    ...

Запускаешь только unit-тесты? pytest -m unit. Только интеграционные? pytest -m integration.

Автоматизация проверок: CI/CD для качества

Локально всё работает? Отлично. Но когда разработчик пушит код в репозиторий, нужна система проверок, которая не позволит мусору попасть в main.

GitHub Actions (или GitLab CI) — это просто:

# .github/workflows/quality.yml
name: Code Quality

on: [push, pull_request]

jobs:
  quality:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.11'
      
      - name: Install dependencies
        run: |
          pip install -r requirements.txt
          pip install mypy ruff black pytest
      
      - name: Run Black
        run: black --check src/
      
      - name: Run Ruff
        run: ruff check src/
      
      - name: Type check with mypy
        run: mypy src/
      
      - name: Run tests
        run: pytest tests/ --cov=src/ --cov-fail-under=80

Теперь каждый PR проходит через эти проверки. Не прошёл тест? Не мёржу. Не покрыто 80% тестами? Не мёржу. Типы не совпадают? Не мёржу.

Это дисциплина. И она работает.

Автоматическая проверка с AI

Знаешь, что я нашёл за последний год? Code review на 80% — это рутина. Проверка type hints, поиск очевидных проблем, стиль кода... Это может делать нейросеть.

Distiq как раз для этого. Это AI-бот, который смотрит каждый MR/PR и говорит: "Эй, здесь уязвимость", "Здесь неэффективный алгоритм", "Здесь type hint неправильный". Инлайн-комментарии в самом коде.

Интегрируется за 2 минуты — добавляешь webhook, и всё. Поддерживает Python, JavaScript, Java, Go... Работает с GitHub, GitLab, GitVerse.

Честно? Когда я впервые видел такое в действии, подумал: "Почему это не было раньше?" Экономит реально часы на code review каждую неделю.


Итак, качество кода Python — это не магия и не талант. Это система. Type hints, структурированный проект, автоматические проверки, тесты, CI/CD. Добавь всё это — и твой код станет надёжным, поддерживаемым, масштабируемым.

Начни с малого. Добавь type hints к одному модулю. Настрой pre-commit. Напиши несколько unit-тестов. Потом расширяй. За месяц это станет привычкой. За полгода — культурой команды.

И да, если ты работаешь в команде, попробуй Distiq. Автоматический code review — это не баловство, это инвестиция в качество. Особенно если нужна проверка кода python online без лишних заморочек.

Попробуйте Distiq для автоматического code review

AI-бот анализирует каждый MR/PR и оставляет комментарии с замечаниями. Интеграция за 2 минуты.

Попробовать бесплатно

Похожие статьи