💻 Développement

dev-python-best-practices

Bonnes pratiques Python pour code propre, performant et maintenable.

⚡ Installation & lancement en 1 commande

Copiez-collez dans votre terminal : le skill s'installe dans ~/.claude/skills et Claude Code se lance directement dessus.

macOS / Linux
curl -fsSL https://raw.githubusercontent.com/khalilbenaz/claude-skills-collection/main/install.sh | sh -s -- dev-python-best-practices --launch
Windows (PowerShell)
iex "& { $(iwr -useb https://raw.githubusercontent.com/khalilbenaz/claude-skills-collection/main/install.ps1) } dev-python-best-practices -Launch"

🚀 Déjà installé ?

claude "/dev-python-best-practices"

Ou tapez /dev-python-best-practices dans une session Claude Code, ou décrivez simplement votre besoin — le skill se déclenche automatiquement via le skill-router.

🔑 Déclencheurs automatiques

Le skill s'active automatiquement quand votre demande contient :

PythonPEP 8pythonicvirtualenvpippoetryFastAPIDjangoasynciotype hints

📦 Installation manuelle

git clone https://github.com/khalilbenaz/claude-skills-collection.git cp -r claude-skills-collection/skills/dev-python-best-practices ~/.claude/skills/

Payload du plugin : skills/dev-python-best-practices · source éditable : dev-skills/python-best-practices

đź“– Manuel

Python Best Practices

1. Structure de projet

Adopter le src layout pour éviter les imports parasites :

mon_projet/
├── src/
│   └── mon_package/
│       ├── __init__.py
│       └── core.py
├── tests/
├── pyproject.toml
└── README.md

Fichier pyproject.toml comme source unique de vérité :

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "mon-package"
version = "1.2.0"
requires-python = ">=3.12"
dependencies = ["httpx>=0.27"]

[project.optional-dependencies]
dev = ["pytest", "ruff", "mypy"]

[tool.ruff]
line-length = 100
select = ["E", "F", "I", "UP", "B", "SIM"]

[tool.mypy]
strict = true

Choix gestionnaire de packages :

uv init mon-projet && cd mon-projet
uv add httpx
uv add --dev pytest ruff mypy
uv run pytest

2. Style et qualité

# Lint + format en une commande
uv run ruff check --fix src/ tests/
uv run ruff format src/ tests/

# Vérification des types
uv run mypy src/ --strict

Type hints obligatoires sur toutes les signatures publiques :

from collections.abc import Sequence
from typing import TypeAlias

UserId: TypeAlias = int

def get_users(ids: Sequence[UserId], active: bool = True) -> list[dict[str, str]]:
    ...

Docstrings format Google (standard de facto pour FastAPI/Django) :

def process(data: bytes, *, encoding: str = "utf-8") -> str:
    """Décode les données brutes en chaîne.

    Args:
        data: Payload binaire à décoder.
        encoding: Encodage cible. Défaut UTF-8.

    Returns:
        Chaîne décodée.

    Raises:
        UnicodeDecodeError: Si l'encodage est incompatible.
    """

3. Patterns pythoniques

# Préférer
actifs = [u for u in users if u.active]

# Générateur pour gros volumes (lazy, O(1) mémoire)
def lignes_valides(fichier: str):
    with open(fichier) as f:
        yield from (l.strip() for l in f if l.strip())

# Dataclass plutĂ´t qu'un dict ou tuple anonyme
from dataclasses import dataclass, field

@dataclass(frozen=True, slots=True)
class Point:
    x: float
    y: float
    tags: list[str] = field(default_factory=list)

# Context manager personnalisé
from contextlib import contextmanager

@contextmanager
def timer(label: str):
    import time
    t = time.perf_counter()
    yield
    print(f"{label}: {time.perf_counter() - t:.3f}s")

4. Async Python

import asyncio
import httpx

async def fetch_all(urls: list[str]) -> list[str]:
    async with httpx.AsyncClient(timeout=10) as client:
        # TaskGroup (Python 3.11+) : annule tout si une tâche échoue
        async with asyncio.TaskGroup() as tg:
            tasks = [tg.create_task(client.get(url)) for url in urls]
    return [t.result().text for t in tasks]

# Appel de code bloquant depuis async
import asyncio
loop = asyncio.get_running_loop()
result = await loop.run_in_executor(None, blocking_function, arg)

Critères de décision async vs threads vs multiprocessing :

ChargeOutil recommandé
I/O concurrente (HTTP, DB)asyncio + librairie async
Code CPU-boundmultiprocessing ou concurrent.futures.ProcessPoolExecutor
Bibliothèque sync non-portablerun_in_executor dans async
Simple parallélisme I/O légerthreading.Thread ou ThreadPoolExecutor

5. Testing

uv run pytest tests/ -v --cov=src --cov-report=term-missing
import pytest
from unittest.mock import AsyncMock, patch

@pytest.fixture
def user():
    return {"id": 1, "name": "Alice"}

@pytest.mark.parametrize("n,expected", [(0, 1), (5, 120)])
def test_factorielle(n: int, expected: int):
    assert factorielle(n) == expected

@pytest.mark.asyncio
async def test_fetch():
    with patch("httpx.AsyncClient.get", new_callable=AsyncMock) as mock:
        mock.return_value.text = "ok"
        result = await fetch("https://example.com")
    assert result == "ok"

Seuil de couverture minimal : ≥ 85 % sur le code métier ; ne pas cibler 100 % sur les adapters.

6. Performance

from functools import cache
import cProfile

# Mémoïsation
@cache
def fib(n: int) -> int:
    return n if n < 2 else fib(n - 1) + fib(n - 2)

# Profiler avant d'optimiser
cProfile.run("mon_algo(grand_dataset)", sort="cumtime")

Outils de profiling :

__slots__ pour réduire l'empreinte mémoire sur des milliers d'instances :

class Vecteur:
    __slots__ = ("x", "y")
    def __init__(self, x: float, y: float) -> None:
        self.x, self.y = x, y

7. Packaging et CI

# Build + publish
uv build
uv publish  # ou twine upload dist/*

# Versioning sémantique
uv run commitizen bump --changelog

GitHub Actions minimal :

# .github/workflows/ci.yml
on: [push, pull_request]
jobs:
  ci:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: astral-sh/setup-uv@v4
      - run: uv sync --dev
      - run: uv run ruff check src/ tests/
      - run: uv run mypy src/
      - run: uv run pytest --cov=src

pre-commit hooks :

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.5.0
    hooks:
      - id: ruff
        args: [--fix]
      - id: ruff-format

8. Pièges et anti-patterns

Anti-patternProblèmeCorrection
except Exception: passAvale les erreurs silencieusementexcept Exception as e: raise RuntimeError("ctx") from e
Argument mutable par défaut def f(lst=[])Partagé entre appelsdef f(lst=None): lst = lst or []
import *Pollution du namespaceImport explicite
requests en async contextBloque l'event loophttpx.AsyncClient
Type hints Dict, List (majuscule)Obsolète depuis Python 3.9dict, list, set minuscules
Chaîner .format() au lieu de f-stringsLisibilitéf"Bonjour {nom}"
os.path partoutVerbeuxpathlib.Path
Ignorer mypy warningsBugs Ă  runtimeCorriger ou annoter # type: ignore[...]

9. Garde-fous en 2026