Communitygithub.com

python-patterns

Idiomes pythoniques, normes PEP 8, annotations de type et bonnes pratiques pour créer des applications Python robustes, efficaces et maintenables. À utiliser lors de l'écriture ou de la révision de code Python pour des questions de structure idiomatique, de typage ou de conformité PEP 8.

Qu'est-ce que python-patterns ?

Cette compétence est conçue pour les développeurs et agents IA travaillant avec Python, en particulier ceux utilisant des plateformes comme Claude Code, Cursor ou Codex. Elle se concentre sur les motifs idiomatiques Python et les bonnes pratiques, couvrant des domaines clés comme l'écriture et la révision de code. Les principes fondamentaux incluent la lisibilité, l'explicite préféré à l'implicite, et la philosophie EAFP (il est plus facile de demander pardon que la permission). Les sujets abordés comprennent les annotations de type modernes (Python 3.9+), la gestion des erreurs avec des exceptions spécifiques et le chaînage d'exceptions, l'utilisation de gestionnaires de contexte pour une gestion fiable des ressources, les compréhensions et générateurs pour un traitement efficace des données, et les décorateurs pour enrichir les fonctions. Elle traite également de la concurrence avec le threading, le multiprocessing et l'async/await, ainsi que de l'organisation des packages, de l'optimisation mémoire (__slots__) et de l'intégration d'outils comme black, isort, ruff et mypy. En fournissant des exemples concrets de code bon et mauvais, l'agent peut aider à écrire du code Python propre, maintenable et performant, adapté à tout type de projet.

Compatible avec~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/ECC/tree/main/skills/python-patterns

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Motifs de développement Python

Motifs Python idiomatiques et bonnes pratiques pour créer des applications robustes, efficaces et maintenables.

Quand activer

  • Écrire du nouveau code Python
  • Réviser du code Python
  • Refactoriser du code Python existant
  • Concevoir des packages/modules Python

Principes fondamentaux

1. La lisibilité est primordiale

Python privilégie la lisibilité. Le code doit être évident et facile à comprendre.

# Bien : Clair et lisible
def get_active_users(users: list[User]) -> list[User]:
    """Retourne uniquement les utilisateurs actifs de la liste fournie."""
    return [user for user in users if user.is_active]


# Mal : Astucieux mais confus
def get_active_users(u):
    return [x for x in u if x.a]

2. L'explicite est préférable à l'implicite

Évitez la magie ; soyez clair sur ce que votre code fait.

# Bien : Configuration explicite
import logging

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)

# Mal : Effets de bord cachés
import some_module
some_module.setup()  # Que fait ceci ?

3. EAFP - Il est plus facile de demander pardon que la permission

Python préfère la gestion des exceptions à la vérification des conditions.

# Bien : Style EAFP
def get_value(dictionary: dict, key: str, default_value: Any = None) -> Any:
    try:
        return dictionary[key]
    except KeyError:
        return default_value

# Mal : Style LBYL (Regarde avant de sauter)
def get_value(dictionary: dict, key: str, default_value: Any = None) -> Any:
    if key in dictionary:
        return dictionary[key]
    else:
        return default_value

Annotations de type

Annotations de type de base

from typing import Optional, List, Dict, Any

def process_user(
    user_id: str,
    data: Dict[str, Any],
    active: bool = True
) -> Optional[User]:
    """Traite un utilisateur et retourne l'utilisateur mis à jour ou None."""
    if not active:
        return None
    return User(user_id, data)

Annotations de type modernes (Python 3.9+)

# Python 3.9+ - Utiliser les types natifs
def process_items(items: list[str]) -> dict[str, int]:
    return {item: len(item) for item in items}

# Python 3.8 et antérieur - Utiliser le module typing
from typing import List, Dict

def process_items(items: List[str]) -> Dict[str, int]:
    return {item: len(item) for item in items}

Alias de type et TypeVar

from typing import TypeVar, Union

# Alias de type pour les types complexes
JSON = Union[dict[str, Any], list[Any], str, int, float, bool, None]

def parse_json(data: str) -> JSON:
    return json.loads(data)

# Types génériques
T = TypeVar('T')

def first(items: list[T]) -> T | None:
    """Retourne le premier élément ou None si la liste est vide."""
    return items[0] if items else None

Duck typing basé sur les protocoles

from typing import Protocol

class Renderable(Protocol):
    def render(self) -> str:
        """Rend l'objet en une chaîne de caractères."""

def render_all(items: list[Renderable]) -> str:
    """Rend tous les éléments qui implémentent le protocole Renderable."""
    return "\n".join(item.render() for item in items)

Motifs de gestion des erreurs

Gestion spécifique des exceptions

# Bien : Attraper des exceptions spécifiques
def load_config(path: str) -> Config:
    try:
        with open(path) as f:
            return Config.from_json(f.read())
    except FileNotFoundError as e:
        raise ConfigError(f"Fichier de configuration introuvable : {path}") from e
    except json.JSONDecodeError as e:
        raise ConfigError(f"JSON invalide dans la configuration : {path}") from e

# Mal : Except nu
def load_config(path: str) -> Config:
    try:
        with open(path) as f:
            return Config.from_json(f.read())
    except:
        return None  # Échec silencieux !

Chaînage d'exceptions

def process_data(data: str) -> Result:
    try:
        parsed = json.loads(data)
    except json.JSONDecodeError as e:
        # Chaîne les exceptions pour préserver la trace
        raise ValueError(f"Échec de l'analyse des données : {data}") from e

Hiérarchie d'exceptions personnalisées

class AppError(Exception):
    """Exception de base pour toutes les erreurs de l'application."""
    pass

class ValidationError(AppError):
    """Levée en cas d'échec de validation des entrées."""
    pass

class NotFoundError(AppError):
    """Levée lorsqu'une ressource demandée est introuvable."""
    pass

# Utilisation
def get_user(user_id: str) -> User:
    user = db.find_user(user_id)
    if not user:
        raise NotFoundError(f"Utilisateur introuvable : {user_id}")
    return user

Gestionnaires de contexte

Gestion des ressources

# Bien : Utilisation des gestionnaires de contexte
def process_file(path: str) -> str:
    with open(path, 'r') as f:
        return f.read()

# Mal : Gestion manuelle des ressources
def process_file(path: str) -> str:
    f = open(path, 'r')
    try:
        return f.read()
    finally:
        f.close()

Gestionnaires de contexte personnalisés

from contextlib import contextmanager

@contextmanager
def timer(name: str):
    """Gestionnaire de contexte pour chronométrer un bloc de code."""
    start = time.perf_counter()
    yield
    elapsed = time.perf_counter() - start
    print(f"{name} a pris {elapsed:.4f} secondes")

# Utilisation
with timer("traitement des données"):
    process_large_dataset()

Classes de gestionnaires de contexte

class DatabaseTransaction:
    def __init__(self, connection):
        self.connection = connection

    def __enter__(self):
        self.connection.begin_transaction()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type is None:
            self.connection.commit()
        else:
            self.connection.rollback()
        return False  # Ne pas supprimer les exceptions

# Utilisation
with DatabaseTransaction(conn):
    user = conn.create_user(user_data)
    conn.create_profile(user.id, profile_data)

Compréhensions et générateurs

Compréhensions de listes

# Bien : Compréhension de liste pour des transformations simples
names = [user.name for user in users if user.is_active]

# Mal : Boucle manuelle
names = []
for user in users:
    if user.is_active:
        names.append(user.name)

# Les compréhensions complexes doivent être développées
# Mal : Trop complexe
result = [x * 2 for x in items if x > 0 if x % 2 == 0]

# Bien : Utiliser une fonction génératrice
def filter_and_transform(items: Iterable[int]) -> list[int]:
    result = []
    for x in items:
        if x > 0 and x % 2 == 0:
            result.append(x * 2)
    return result

Expressions génératrices

# Bien : Générateur pour évaluation paresseuse
total = sum(x * x for x in range(1_000_000))

# Mal : Crée une grande liste intermédiaire
total = sum([x * x for x in range(1_000_000)])

Fonctions génératrices

def read_large_file(path: str) -> Iterator[str]:
    """Lit un gros fichier ligne par ligne."""
    with open(path) as f:
        for line in f:
            yield line.strip()

# Utilisation
for line in read_large_file("huge.txt"):
    process(line)

Classes de données et tuples nommés

Classes de données

from dataclasses import dataclass, field
from datetime import datetime

@dataclass
class User:
    """Entité utilisateur avec __init__, __repr__ et __eq__ automatiques."""
    id: str
    name: str
    email: str
    created_at: datetime = field(default_factory=datetime.now)
    is_active: bool = True

# Utilisation
user = User(
    id="123",
    name="Alice",
    email="[email protected]"
)

Classes de données avec validation

@dataclass
class User:
    email: str
    age: int

    def __post_init__(self):
        # Valider le format de l'email
        if "@" not in self.email:
            raise ValueError(f"Email invalide : {self.email}")
        # Valider la tranche d'âge
        if self.age < 0 or self.age > 150:
            raise ValueError(f"Âge invalide : {self.age}")

Tuples nommés

from typing import NamedTuple

class Point(NamedTuple):
    """Point 2D immuable."""
    x: float
    y: float

    def distance(self, other: 'Point') -> float:
        return ((self.x - other.x) ** 2 + (self.y - other.y) ** 2) ** 0.5

# Utilisation
p1 = Point(0, 0)
p2 = Point(3, 4)
print(p1.distance(p2))  # 5.0

Décorateurs

Décorateurs de fonctions

import functools
import time

def timer(func: Callable) -> Callable:
    """Décorateur pour chronométrer l'exécution d'une fonction."""
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = func(*args, **kwargs)
        elapsed = time.perf_counter() - start
        print(f"{func.__name__} a pris {elapsed:.4f}s")
        return result
    return wrapper

@timer
def slow_function():
    time.sleep(1)

# slow_function() affiche : slow_function a pris 1.0012s

Décorateurs paramétrés

def repeat(times: int):
    """Décorateur pour répéter une fonction plusieurs fois."""
    def decorator(func: Callable) -> Callable:
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            results = []
            for _ in range(times):
                results.append(func(*args, **kwargs))
            return results
        return wrapper
    return decorator

@repeat(times=3)
def greet(name: str) -> str:
    return f"Bonjour, {name} !"

# greet("Alice") retourne ["Bonjour, Alice !", "Bonjour, Alice !", "Bonjour, Alice !"]

Décorateurs basés sur des classes

class CountCalls:
    """Décorateur qui compte combien de fois une fonction est appelée."""
    def __init__(self, func: Callable):
        functools.update_wrapper(self, func)
        self.func = func
        self.count = 0

    def __call__(self, *args, **kwargs):
        self.count += 1
        print(f"{self.func.__name__} a été appelée {self.count} fois")
        return self.func(*args, **kwargs)

@CountCalls
def process():
    pass

# Chaque appel à process() affiche le nombre d'appels

Motifs de concurrence

Threading pour les tâches liées aux entrées/sorties

import concurrent.futures
import threading

def fetch_url(url: str) -> str:
    """Récupère une URL (opération liée aux E/S)."""
    import urllib.request
    with urllib.request.urlopen(url) as response:
        return response.read().decode()

def fetch_all_urls(urls: list[str]) -> dict[str, str]:
    """Récupère plusieurs URL simultanément en utilisant des threads."""
    with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
        future_to_url = {executor.submit(fetch_url, url): url for url in urls}
        results = {}
        for future in concurrent.futures.as_completed(future_to_url):
            url = future_to_url[future]
            try:
                results[url] = future.result()
            except Exception as e:
                results[url] = f"Erreur : {e}"
    return results

Multiprocessing pour les tâches liées au CPU

def process_data(data: list[int]) -> int:
    """Calcul intensif pour le CPU."""
    return sum(x ** 2 for x in data)

def process_all(datasets: list[list[int]]) -> list[int]:
    """Traite plusieurs ensembles de données en utilisant plusieurs processus."""
    with concurrent.futures.ProcessPoolExecutor() as executor:
        results = list(executor.map(process_data, datasets))
    return results

Async/Await pour les E/S concurrentes

import asyncio

async def fetch_async(url: str) -> str:
    """Récupère une URL de manière asynchrone."""
    import aiohttp
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            return await response.text()

async def fetch_all(urls: list[str]) -> dict[str, str]:
    """Récupère plusieurs URL simultanément."""
    tasks = [fetch_async(url) for url in urls]
    results = await asyncio.gather(*tasks, return_exceptions=True)
    return dict(zip(urls, results))

Organisation des packages

Structure standard du projet

myproject/
├── src/
│   └── mypackage/
│       ├── __init__.py
│       ├── main.py
│       ├── api/
│       │   ├── __init__.py
│       │   └── routes.py
│       ├── models/
│       │   ├── __init__.py
│       │   └── user.py
│       └── utils/
│           ├── __init__.py
│           └── helpers.py
├── tests/
│   ├── __init__.py
│   ├── conftest.py
│   ├── test_api.py
│   └── test_models.py
├── pyproject.toml
├── README.md
└── .gitignore

Conventions d'importation

# Bien : Ordre d'importation - bibliothèque standard, tiers, local
import os
import sys
from pathlib import Path

import requests
from fastapi import FastAPI

from mypackage.models import User
from mypackage.utils import format_name

# Bien : Utiliser isort pour le tri automatique des imports
# pip install isort

init.py pour les exports de packages

# mypackage/__init__.py
"""mypackage - Un exemple de package Python."""

__version__ = "1.0.0"

# Exporter les classes/fonctions principales au niveau du package
from mypackage.models import User, Post
from mypackage.utils import format_name

__all__ = ["User", "Post", "format_name"]

Mémoire et performances

Utiliser slots pour l'efficacité mémoire

# Mal : Une classe normale utilise __dict__ (plus de mémoire)
class Point:
    def __init__(self, x: float, y: float):
        self.x = x
        self.y = y

# Bien : __slots__ réduit l'utilisation mémoire
class Point:
    __slots__ = ['x', 'y']

    def __init__(self, x: float, y: float):
        self.x = x
        self.y = y

Générateur pour les grandes données

# Mal : Retourne une liste complète en mémoire
def read_lines(path: str) -> list[str]:
    with open(path) as f:
        return [line.strip() for line in f]

# Bien : Produit les lignes une par une
def read_lines(path: str) -> Iterator[str]:
    with open(path) as f:
        for line in f:
            yield line.strip()

Éviter la concaténation de chaînes dans les boucles

# Mal : O(n²) en raison de l'immuabilité des chaînes
result = ""
for item in items:
    result += str(item)

# Bien : O(n) avec join
result = "".join(str(item) for item in items)

# Bien : Utiliser StringIO pour la construction
from io import StringIO

buffer = StringIO()
for item in items:
    buffer.write(str(item))
result = buffer.getvalue()

Intégration des outils Python

Commandes essentielles

# Formatage du code
black .
isort .

# Linting
ruff check .

# Vérification de type
mypy .

# Tests
pytest
pytest --cov=src

# Dépendances (gestion

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/content-engine

Create platform-native content systems for X, LinkedIn, TikTok, YouTube, newsletters, and repurposed multi-platform campaigns. Use when the user wants social posts, threads, scripts, content calendars, or one source asset adapted cleanly across platforms.

affaan-m/ECC

Multi-platform content distribution across X, LinkedIn, Threads, and Bluesky. Adapts content per platform using content-engine patterns. Never posts identical content cross-platform. Use when the user wants to distribute content across social platforms.

affaan-m/fal-ai-media

Unified media generation via fal.ai MCP — image, video, and audio. Covers text-to-image (Nano Banana), text/image-to-video (Seedance, Kling, Veo 3), text-to-speech (CSM-1B), and video-to-audio (ThinkSound). Use when the user wants to generate images, videos, or audio with AI.

affaan-m/manim-video

日本語翻訳:このファイルは manim-video 用の日本語翻訳が必要です

affaan-m/remotion-video-creation

Remotion のベストプラクティス - React で動画を作成する。3D、アニメーション、音声、字幕、チャート、トランジションなどをカバーするドメイン固有の29のルール。

affaan-m/video-editing

AI-assisted video editing workflows for cutting, structuring, and augmenting real footage. Covers the full pipeline from raw capture through FFmpeg, Remotion, ElevenLabs, fal.ai, and final polish in Descript or CapCut. Use when the user wants to edit video, cut footage, create vlogs, or build video content.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

agent-self-evaluation

Use after completing any non-trivial task. The agent self-rates its output on 5 axes — accuracy, completeness, clarity, actionability, conciseness — with concrete evidence per criterion. Produces a structured 1-5 scorecard with specific improvement suggestions.

agent-sort

Build an evidence-backed ECC install plan for a specific repo by sorting skills, commands, rules, hooks, and extras into DAILY vs LIBRARY buckets using parallel repo-aware review passes. Use when ECC should be trimmed to what a project actually needs instead of loading the full bundle.

ai-first-engineering

Engineering operating model for teams where AI agents generate a large share of implementation output. Use when setting team process, review gates, or ownership rules for a codebase largely written by agents.

ai-regression-testing

Regression testing strategies for AI-assisted development. Sandbox-mode API testing without database dependencies, automated bug-check workflows, and patterns to catch AI blind spots where the same model writes and reviews code. Use when adding regression coverage to AI-assisted code, or when the same model both wrote and reviewed a change.

android-clean-architecture

Clean Architecture patterns for Android and Kotlin Multiplatform projects — module structure, dependency rules, UseCases, Repositories, and data layer patterns. Use when structuring modules, layers, or data flow in an Android or KMP project.

angular-developer

Generates Angular code and provides architectural guidance. Trigger when creating projects, components, or services, or for best practices on reactivity (signals, linkedSignal, resource), forms, dependency injection, routing, SSR, accessibility (ARIA), animations, styling (component styles, Tailwind CSS), testing, or CLI tooling.

Skills associés