PEP 8 и линтеры — чистый код на Python
PEP 8 — стандарт стиля
PEP 8 — официальное руководство по стилю кода Python.
Отступы и длина строк
# GOOD: 4 spaces per indentation level
def calculate_total(
items: list[dict],
tax_rate: float = 0.20,
) -> float:
total = sum(item["price"] * item["qty"] for item in items)
return total * (1 + tax_rate)
# Max 79 characters per line (or 88 for Black)
long_variable = (
first_value
+ second_value
- third_value
)
Именование
# Variables and functions: snake_case
user_name = "Алексей"
def get_user_by_id(user_id: int) -> dict: ...
# Constants: UPPER_SNAKE_CASE
MAX_RETRIES = 3
DATABASE_URL = "postgresql://localhost/mydb"
# Classes: PascalCase
class UserService: ...
class HTTPClient: ... # Abbreviations in caps
# Private: prefix with underscore
class Account:
def __init__(self) -> None:
self._balance = 0.0 # Protected by convention
def _internal_method(self) -> None: ...
Импорты
# Grouped and sorted:
# 1. Standard library
import os
import sys
from datetime import datetime
# 2. Third-party packages
import httpx
from pydantic import BaseModel
# 3. Local imports
from myapp.models import User
# BAD: wildcard imports
# from os import * # Never!
ruff — быстрый линтер и форматтер
ruff — замена flake8, isort, pyupgrade и black. Написан на Rust, работает в 10-100 раз быстрее.
pip install ruff
ruff check . # Lint
ruff check --fix . # Auto-fix
ruff format . # Format
# pyproject.toml
[tool.ruff]
target-version = "py312"
line-length = 88
[tool.ruff.lint]
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # pyflakes
"I", # isort
"N", # pep8-naming
"UP", # pyupgrade
"B", # flake8-bugbear
"SIM", # flake8-simplify
"C4", # flake8-comprehensions
"RUF", # Ruff-specific rules
]
[tool.ruff.lint.isort]
known-first-party = ["myapp"]
Что обнаруживает ruff:
# F841: unused variable
x = 42 # never used
# E711: comparison to None
if value == None: ... # Use 'is None'
# SIM108: use ternary
if cond:
result = "yes"
else:
result = "no"
# Better: result = "yes" if cond else "no"
# UP035: deprecated import
from typing import List # Use list (3.9+)
black — форматтер кода
pip install black
black . # Format
black --check . # Check without changing
Замечание: ruff format может полностью заменить black.
mypy — проверка типов
pip install mypy
mypy src/
[tool.mypy]
python_version = "3.12"
strict = true
def add(a: int, b: int) -> int:
return a + b
result: str = add(1, 2) # mypy error: incompatible types
greet(42) # mypy error: expected str
pre-commit — автоматизация проверок
pip install pre-commit
# .pre-commit-config.yaml
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.8.0
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.13.0
hooks:
- id: mypy
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
pre-commit install # Install hooks
pre-commit run --all-files # Run manually
Рекомендуемая конфигурация
# pyproject.toml
[tool.ruff]
target-version = "py312"
line-length = 88
[tool.ruff.lint]
select = ["E", "W", "F", "I", "N", "UP", "B", "SIM", "C4", "RUF"]
[tool.mypy]
python_version = "3.12"
strict = true
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-v --strict-markers"
Резюме инструментов
| Инструмент | Назначение | Замена ruff |
|---|---|---|
| ruff | Линтинг + форматирование | - |
| black | Форматирование | ruff format |
| isort | Сортировка импортов | ruff check --select I |
| flake8 | Линтинг стиля | ruff check |
| mypy | Проверка типов | Нет замены |
| pre-commit | Автоматизация | Нет замены |