Если ты пишешь код на 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 без лишних заморочек.
