MFormations
Modern Python Engineering

Chapitre 23

Chapitre 23 : Annexes

> **Glossaire, cheat sheets, idioms Python, et erreurs fréquentes** — tout ce qu'il vous faut en référence rapide.

Chapitre 23 — Annexes

Version : 1.0.0
Glossaire (200+ termes), cheat sheets (5), idioms (20), erreurs fréquentes (30).


Table des matières

  1. Glossaire
  2. Cheat Sheets
  3. Python Idioms
  4. Common Mistakes

1. Glossaire

A

ABC (Abstract Base Class) : Classe qui ne peut pas être instanciée directement. Utilisée pour définir une interface. Module abc.

from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self) -> float: ...

Abstract Syntax Tree (AST) : Représentation arborescente du code source Python. Utilisée par les linters, formatters, et outils d'analyse.

import ast
tree = ast.parse("x = 1 + 2")

Aggregate Root : Entité racine dans DDD qui garantit la cohérence d'un aggregate.

Alembic : Outil de migration de base de données pour SQLAlchemy.

Annotation : Syntaxe pour indiquer le type des variables, paramètres et retours (PEP 484).

AnyIO : Bibliothèque asynchrone indépendante (compatible asyncio et trio).

Argument : Valeur passée à une fonction lors de l'appel.

ASGI : Asynchronous Server Gateway Interface — successeur de WSGI pour les applications async.

asyncio : Bibliothèque standard de programmation asynchrone (PEP 3156).

Awaitable : Objet pouvant être utilisé avec await. Inclut coroutines, Tasks, Futures.


B

Backpressure : Mécanisme de régulation de flux quand un producteur est plus rapide que le consommateur.

Bandit : Analyseur de sécurité statique pour Python.

BaseException : Classe de base de toutes les exceptions. Éviter de la catch directement.

Batteries included : Philosophie Python — la bibliothèque standard couvre de nombreux besoins.

Bytecode : Code intermédiaire compilé par Python avant exécution. Fichiers .pyc.

BLP (Boris L. Pavlov) : Principe de conception objet.


C

Cache : Stockage temporaire pour accélérer les accès répétés.

Celery : Système de tâches asynchrones distribué.

CI/CD : Continuous Integration / Continuous Deployment.

Class variable : Variable partagée par toutes les instances d'une classe.

Clean Architecture : Architecture logicielle qui sépare le code en couches (entités, use cases, adaptateurs, frameworks).

CLI : Command Line Interface. Interface en ligne de commande.

Closure : Fonction qui capture les variables de son environnement de définition.

Comprehension : Syntaxe concise pour créer des listes, dicts, sets.

squares = [x**2 for x in range(10)]

Context Manager : Objet qui gère les ressources via __enter__ et __exit__.

Coroutine : Fonction asynchrone définie avec async def. Retourne un objet coroutine.

Coverage : Mesure du pourcentage de code exécuté par les tests.

CPython : Implémentation de référence de Python (en C).

CQRS : Command Query Responsibility Segregation — séparation des modèles de lecture et d'écriture.

CRUD : Create, Read, Update, Delete — opérations de base sur des données persistées.

CSRF : Cross-Site Request Forgery — attaque qui force un utilisateur à exécuter des actions non désirées.


D

DAG : Directed Acyclic Graph — graphe orienté sans cycle, utilisé par Airflow.

DataClass : Décorateur qui génère automatiquement __init__, __repr__, __eq__, etc.

from dataclasses import dataclass

@dataclass
class Point:
    x: float
    y: float

Deadlock : Situation où deux threads s'attendent mutuellement, bloquant l'exécution.

Decorator : Fonction qui modifie le comportement d'une autre fonction.

Dependency Injection : Pattern où les dépendances sont fournies de l'extérieur plutôt que créées en interne.

Descriptor : Objet qui implémente __get__, __set__, ou __delete__ pour contrôler l'accès aux attributs.

Docker : Plateforme de conteneurisation.

Docstring : Chaîne de documentation en début de module, classe, ou fonction.

DOM : Document Object Model — représentation en mémoire d'un document XML/HTML.

DRF : Django REST Framework.

DRY : Don't Repeat Yourself — principe d'éviter la duplication de code.

DTO : Data Transfer Object — objet pour transporter des données entre couches.

Duck Typing : "Si ça marche comme un canard et que ça cancane comme un canard, alors c'est un canard." Python privilégie le comportement sur le type.


E

EAFP : Easier to Ask for Forgiveness than Permission. Style Python : essayer, et gérer l'erreur si elle arrive.

try:
    value = my_dict[key]
except KeyError:
    value = default

Encapsulation : Principe de cacher l'implémentation interne d'un objet.

Entry point : Point d'entrée d'une application (ex: app = FastAPI()).

Enum : Type énuméré (PEP 435). Ensemble de valeurs nommées.

from enum import Enum

class Color(Enum):
    RED = 1
    GREEN = 2
    BLUE = 3

Event Loop : Boucle d'événements qui exécute les coroutines et callbacks asynchrones.

Exception : Événement qui interrompt le flux normal d'exécution.


F

F-string : Chaîne formatée (PEP 498). f"Hello {name}".

Factory : Pattern de création d'objets.

FastAPI : Framework web moderne, haut-performant, basé sur Starlette et Pydantic.

Fixture : Fonction pytest qui fournit un contexte reproductible pour les tests.

Flake8 : Linter Python (regroupe PyFlakes, pycodestyle, McCabe).

Fluent interface : Style d'API où les méthodes retournent self pour le chaînage.

Free-threaded mode : Mode Python 3.13 où le GIL est désactivé.

frozenset : Version immuable de set.

Future : Objet qui représente un résultat asynchrone pas encore disponible.


G

Garbage Collector : Collecteur de déchets qui libère la mémoire des objets non utilisés.

Generics : Types paramétrés (PEP 484). list[int], dict[str, float].

Generator : Fonction qui utilise yield pour produire une séquence de valeurs paresseuse.

GIL : Global Interpreter Lock — mutex qui protège l'accès aux objets Python.

Global Interpreter Lock : Voir GIL.

GroupBy : Opération pandas pour regrouper des données.

Gunicorn : Serveur WSGI pour applications Python.


H

Hashable : Objet qui peut être utilisé comme clé de dictionnaire. Doit implémenter __hash__.

HTTPX : Client HTTP asynchrone moderne pour Python.

Hypothesis : Bibliothèque de property-based testing.


I

I/O bound : Tâche limitée par les entrées/sorties (réseau, disque).

id() : Fonction qui retourne l'identifiant mémoire d'un objet.

Idempotent : Opération qui produit le même résultat quand elle est exécutée plusieurs fois.

Immutable : Non modifiable après création. Ex: tuple, str, int, frozenset.

Import : Mécanisme pour charger des modules et packages.

Injection de dépendances : Voir Dependency Injection.

isinstance() : Fonction qui vérifie si un objet est une instance d'une classe.

Iterable : Objet qui implémente __iter__ ou __getitem__.

Iterator : Objet qui implémente __iter__ et __next__.


J

Jinja2 : Moteur de templates pour Python.

JIT : Just-In-Time compilation — compilation à l'exécution pour améliorer les performances.

JSON : JavaScript Object Notation — format d'échange de données léger.

JWT : JSON Web Token — format de token d'authentification.


K

kwargs : Keyword arguments — dictionnaire des arguments nommés (**kwargs).


L

Lambda : Fonction anonyme à une seule expression. lambda x: x * 2.

LBYL : Look Before You Leap. Style opposé à EAFP : vérifier avant d'agir.

List comprehension : Compréhension de liste (voir Comprehension).

Linter : Outil d'analyse statique qui détecte les erreurs potentielles.

LRU Cache : Least Recently Used cache — évince les éléments les moins récemment utilisés.


M

MagicMock : Classe de mock automatique dans unittest.mock.

Metaclass : Classe de classe. Personnalise la création des classes.

Middleware : Logiciel qui agit comme pont entre l'application et le serveur.

Migration : Changement de schéma de base de données (via Alembic).

Mixin : Classe qui fournit des méthodes supplémentaires sans être utilisée seule.

Mock : Objet simulé pour les tests, qui imite le comportement d'un objet réel.

Module : Fichier .py contenant du code Python.

Monkey Patching : Modification dynamique de classes ou modules à l'exécution.

MRO : Method Resolution Order — ordre de résolution des méthodes dans l'héritage multiple.

Mutable : Modifiable après création. Ex: list, dict, set.

mypy : Vérificateur de types statique optionnel pour Python.


N

Namespace : Espace de noms qui mappe les noms aux objets.

Name mangling : Transformation des noms d'attributs privés (__attr_Class__attr).

NaN : Not a Number — valeur manquante dans pandas/numpy.

numpy : Bibliothèque de calcul numérique en Python.


O

Object : Instance d'une classe. Base de tous les types en Python.

OOP : Object-Oriented Programming.

OpenAPI : Spécification pour les API REST (anciennement Swagger).

Operator overloading : Surcharge d'opérateurs via des méthodes spéciales (__add__, __eq__, etc.).

ORM : Object-Relational Mapping — mapping objet-relationnel.

Overfitting : Modèle qui apprend trop bien les données d'entraînement mais généralise mal.


P

Package : Dossier contenant __init__.py et des modules Python.

pandas : Bibliothèque de manipulation et analyse de données.

Parameter : Variable définie dans la signature d'une fonction.

Parametrize : Décorateur pytest pour exécuter un test avec plusieurs jeux de données.

PEP : Python Enhancement Proposal — proposition d'amélioration de Python.

Pipeline : Enchaînement d'étapes de traitement. Utilisé par scikit-learn, pandas, CI/CD.

Polymorphism : Capacité d'un objet à se comporter différemment selon son type.

Property : Attribut contrôlé par des getters/setters avec @property.

Protocol : Classe qui définit une interface pour le duck typing statique (PEP 544).

PSF : Python Software Foundation.

Pydantic : Bibliothèque de validation de données basée sur les type hints.

PyPI : Python Package Index — dépôt officiel des packages Python.

pytest : Framework de test pour Python.

Pythonic : Code qui suit les conventions et idiomes de Python.


Q-R

Query : Requête (base de données, API).

Race condition : Situation où le comportement dépend de l'ordre d'exécution non déterministe.

Recursion : Fonction qui s'appelle elle-même.

Reference counting : Comptage de références pour la gestion mémoire.

Repository : Pattern qui abstrait la persistance des données.

REST : Representational State Transfer — style d'architecture pour les API web.

ruff : Linter et formatter Python ultra-rapide écrit en Rust.


S

scikit-learn : Bibliothèque de machine learning pour Python.

Scope : Portée d'une variable. LEGB : Local, Enclosing, Global, Built-in.

Sentinel : Valeur spéciale utilisée comme marqueur (ex: object() pour détecter "non fourni").

Serialization : Conversion d'un objet en format transportable (JSON, pickle).

Set : Collection non ordonnée d'éléments uniques.

Singleton : Pattern où une classe n'a qu'une seule instance.

Slice : Syntaxe pour extraire une partie d'une séquence. seq[start:stop:step].

SOLID : Cinq principes de conception orientée objet.

SQLAlchemy : ORM et toolkit SQL pour Python.

Starlette : Framework ASGI léger, base de FastAPI.

Static method : Méthode qui n'accède ni à l'instance ni à la classe (@staticmethod).

Syn责任 : Blocage. Opération synchrone = bloquante.


T

Task : Coroutine en cours d'exécution gérée par l'event loop.

Thread : Unité d'exécution légère. Limitée par le GIL pour le CPU.

Timeout : Limite de temps pour une opération.

Tri : Opération de tri. sorted(), list.sort().

tuple : Séquence immuable.

Type hint : Annotation de type optionnelle. name: str = "World".

Typer : Bibliothèque CLI basée sur les type hints.


U-Z

UML : Unified Modeling Language — langage de modélisation.

Unit of Work : Pattern qui maintient la cohérence des changements dans un contexte transactionnel.

Uvicorn : Serveur ASGI pour applications async Python.

Virtual environment : Environnement Python isolé.

WebSocket : Protocole de communication bidirectionnelle temps réel.

WSGI : Web Server Gateway Interface — interface standard pour les applications web synchrones.

WSGI : Voir WSGI.

XSS : Cross-Site Scripting — injection de scripts malveillants.

Yield : Mot-clé qui transforme une fonction en générateur.

Zen of Python : 19 principes directeurs de Python. import this.

Zip : Fonction qui agrège des itérables élément par élément.


2. Cheat Sheets

Cheat Sheet — pandas

import pandas as pd
import numpy as np

# Création
df = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})
s = pd.Series([1, 2, 3], name='col')

# Lecture/Écriture
df.to_csv('file.csv', index=False)
df.to_parquet('file.parquet')
df = pd.read_csv('file.csv')
df = pd.read_parquet('file.parquet')

# Inspection
df.head(10)              # Premières lignes
df.info()                # Types, mémoire
df.describe()            # Statistiques
df.shape                 # Dimensions (rows, cols)
df.columns               # Noms de colonnes
df.dtypes                # Types par colonne

# Sélection
df['col']                # Une colonne → Series
df[['col1', 'col2']]    # Plusieurs colonnes → DataFrame
df.loc[0]                # Par label (ligne 0)
df.iloc[0]               # Par position
df.query('A > 2')        # Avec expression
df.filter(like='pattern') # Filtre les colonnes par nom

# Nettoyage
df.dropna()              # Supprime NaN
df.fillna(0)             # Remplace NaN
df.drop_duplicates()     # Supprime doublons
df.rename(columns={'A': 'new_A'})
df['A'].astype(float)    # Convertit le type

# GroupBy
df.groupby('A').sum()
df.groupby('A').agg({'B': 'sum', 'C': 'mean'})
df.groupby('A').transform('rank')

# Merge
pd.merge(df1, df2, on='key', how='left')
pd.concat([df1, df2], axis=0)

# Time Series
df['date'] = pd.to_datetime(df['date'])
df.set_index('date', inplace=True)
df.resample('D').mean()       # Rééchantillonage
df.rolling(7).mean()          # Fenêtre glissante
df.shift(1)                   # Décalage (lag)

# Apply / Map
df['A'].apply(lambda x: x * 2)
df['category'] = df['A'].map({1: 'low', 2: 'high'})

# Pivot
pd.pivot_table(df, values='sales', index='region', columns='year')
df.melt(id_vars=['id'], var_name='year', value_name='sales')

Cheat Sheet — FastAPI

from fastapi import FastAPI, HTTPException, Depends, Query, WebSocket
from fastapi.security import OAuth2PasswordBearer
from pydantic import BaseModel, Field
from typing import Annotated

app = FastAPI(title="My API", version="1.0.0")

# --- Modèles Pydantic ---
class Item(BaseModel):
    name: str = Field(..., min_length=1, max_length=100)
    price: float = Field(..., gt=0)
    in_stock: bool = True

class ItemResponse(Item):
    id: int

# --- Routes CRUD ---
@app.post("/items", response_model=ItemResponse, status_code=201)
async def create_item(item: Item) -> ItemResponse:
    ...

@app.get("/items")
async def list_items(
    page: int = Query(1, ge=1),
    size: int = Query(20, ge=1, le=100),
) -> list[ItemResponse]:
    ...

@app.get("/items/{item_id}")
async def get_item(item_id: int) -> ItemResponse:
    if item_id not in items:
        raise HTTPException(status_code=404, detail="Item not found")
    return items[item_id]

@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item) -> ItemResponse:
    ...

@app.delete("/items/{item_id}", status_code=204)
async def delete_item(item_id: int) -> None:
    ...

# --- Dépendances ---
async def pagination(
    page: int = Query(1, ge=1),
    size: int = Query(20, ge=1, le=100),
) -> tuple[int, int]:
    return (page, size)

@app.get("/paginated")
async def paginated_endpoint(
    pag: tuple[int, int] = Depends(pagination),
) -> dict:
    page, size = pag
    return {"page": page, "size": size}

# --- Sécurité ---
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/login")

@app.post("/auth/login")
async def login(username: str, password: str) -> dict:
    ...

@app.get("/protected")
async def protected_route(token: str = Depends(oauth2_scheme)) -> dict:
    return {"token": token}

# --- WebSocket ---
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket) -> None:
    await websocket.accept()
    while True:
        data = await websocket.receive_text()
        await websocket.send_text(f"Echo: {data}")

# --- Configuration ---
@app.on_event("startup")
async def startup() -> None:
    # Initialiser DB, cache, etc.
    pass

@app.on_event("shutdown")
async def shutdown() -> None:
    # Fermer connexions
    pass

# --- Healthcheck ---
@app.get("/health")
async def health() -> dict:
    return {"status": "ok"}

Cheat Sheet — pytest

import pytest
from unittest.mock import Mock, patch, MagicMock

# --- Fixtures ---
@pytest.fixture
def sample_data() -> dict:
    return {"key": "value"}

@pytest.fixture(scope="module")
def heavy_resource():
    # setup
    resource = create_expensive_resource()
    yield resource
    # teardown
    resource.close()

@pytest.fixture(autouse=True)
def auto_fixture():
    """Appliqué à tous les tests du fichier"""
    pass

# --- Tests ---
def test_basic(sample_data: dict) -> None:
    assert sample_data["key"] == "value"

def test_exception() -> None:
    with pytest.raises(ValueError, match="invalid"):
        raise ValueError("invalid value")

def test_approx() -> None:
    assert 0.1 + 0.2 == pytest.approx(0.3)

# --- Paramétrage ---
@pytest.mark.parametrize("input_val,expected", [
    (1, 2),
    (2, 4),
    (3, 6),
], ids=["one", "two", "three"])
def test_double(input_val: int, expected: int) -> None:
    assert input_val * 2 == expected

# --- Mocking ---
def test_with_mock() -> None:
    mock = MagicMock()
    mock.get_data.return_value = {"result": 42}
    assert mock.get_data() == {"result": 42}

@patch("module.requests.get")
def test_api_call(mock_get: MagicMock) -> None:
    mock_get.return_value.json.return_value = {"ok": True}
    # Le code qui appelle requests.get utilise le mock
    ...

# --- Marks ---
@pytest.mark.slow
def test_slow_operation() -> None:
    ...

@pytest.mark.skip(reason="Not implemented yet")
def test_future() -> None:
    ...

@pytest.mark.skipif(sys.version_info < (3, 12), reason="Requires 3.12+")
def test_python_version() -> None:
    ...

# --- Fixtures avancées ---
@pytest.fixture
def mock_db(mocker):
    return mocker.patch("module.Database")

@pytest.mark.asyncio
async def test_async_function() -> None:
    result = await async_function()
    assert result == "expected"

# --- Temporary files ---
def test_temp_file(tmp_path) -> None:
    d = tmp_path / "subdir"
    d.mkdir()
    f = d / "test.txt"
    f.write_text("hello")
    assert f.read_text() == "hello"

# --- Capture stdout ---
def test_output(capsys) -> None:
    print("Hello")
    captured = capsys.readouterr()
    assert captured.out == "Hello\n"

Cheat Sheet — SQLAlchemy 2.0

from sqlalchemy import create_engine, text, select, func
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
from sqlalchemy.orm import relationship, Session, sessionmaker
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession

# --- Configuration ---
engine = create_engine("postgresql+psycopg://user:pass@localhost/db")
async_engine = create_async_engine(
    "postgresql+asyncpg://user:pass@localhost/db"
)

SessionLocal = sessionmaker(engine, class_=Session)
AsyncSessionLocal = sessionmaker(async_engine, class_=AsyncSession)

# --- Modèles ---
class Base(DeclarativeBase):
    pass

class User(Base):
    __tablename__ = "users"

    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column(unique=True)
    email: Mapped[str]
    posts: Mapped[list["Post"]] = relationship(back_populates="author")

class Post(Base):
    __tablename__ = "posts"

    id: Mapped[int] = mapped_column(primary_key=True)
    title: Mapped[str]
    body: Mapped[str]
    author_id: Mapped[int] = mapped_column(ForeignKey("users.id"))
    author: Mapped["User"] = relationship(back_populates="posts")

# --- CRUD (sync) ---
with Session(engine) as session:
    # Create
    user = User(name="Alice", email="alice@example.com")
    session.add(user)
    session.commit()

    # Read
    user = session.get(User, 1)
    users = session.scalars(select(User)).all()
    user = session.scalar(
        select(User).where(User.name == "Alice")
    )

    # Update
    user.name = "Alicia"
    session.commit()

    # Delete
    session.delete(user)
    session.commit()

# --- CRUD (async) ---
async with AsyncSession(async_engine) as session:
    user = User(name="Bob", email="bob@example.com")
    session.add(user)
    await session.commit()

    result = await session.execute(
        select(User).where(User.name == "Bob")
    )
    user = result.scalar_one()

# --- Relations ---
with Session(engine) as session:
    user = session.get(User, 1)
    print(user.posts)  # Accès lazy

    # Eager loading
    from sqlalchemy.orm import joinedload
    user = session.scalar(
        select(User)
        .options(joinedload(User.posts))
        .where(User.id == 1)
    )

# --- Core (sans ORM) ---
with engine.connect() as conn:
    result = conn.execute(text("SELECT * FROM users"))
    for row in result:
        print(row.name)

    conn.execute(
        text("INSERT INTO users (name) VALUES (:name)"),
        [{"name": "Charlie"}, {"name": "Dave"}],
    )
    conn.commit()

# --- Migrations (Alembic) ---
# alembic init alembic
# alembic revision --autogenerate -m "create users"
# alembic upgrade head

Cheat Sheet — asyncio

import asyncio
from asyncio import Task, Future, Queue, Event

# --- Création et exécution ---
async def main():
    result = await async_function()
    return result

asyncio.run(main())

# --- Tâches ---
async def worker(name: str, delay: float) -> str:
    await asyncio.sleep(delay)
    return f"Worker {name} done"

async def main():
    task = asyncio.create_task(worker("A", 1.0))
    result = await task

    # Tâches en concurrence
    tasks = [asyncio.create_task(worker(n, d)) for n, d in [("A",1), ("B",2)]]
    results = await asyncio.gather(*tasks)

# --- TaskGroup (3.11+) ---
async def main():
    async with asyncio.TaskGroup() as tg:
        t1 = tg.create_task(worker("X", 1))
        t2 = tg.create_task(worker("Y", 2))
    # Toutes les tâches sont terminées ici

# --- Timeout ---
async def main():
    try:
        async with asyncio.timeout(5):
            result = await slow_operation()
    except TimeoutError:
        result = fallback

# --- Queue producteur/consommateur ---
async def producer(queue: asyncio.Queue, n: int) -> None:
    for i in range(n):
        await queue.put(i)
        await asyncio.sleep(0.1)
    await queue.put(None)  # Sentinel

async def consumer(queue: asyncio.Queue) -> None:
    while True:
        item = await queue.get()
        if item is None:
            queue.task_done()
            break
        print(f"Processed {item}")
        queue.task_done()

async def main():
    queue = asyncio.Queue(maxsize=10)
    async with asyncio.TaskGroup() as tg:
        tg.create_task(producer(queue, 5))
        tg.create_task(consumer(queue))

# --- Synchronisation ---
lock = asyncio.Lock()
event = asyncio.Event()
semaphore = asyncio.Semaphore(10)

async def with_lock():
    async with lock:
        # Section critique
        pass

# --- Sous-processus ---
async def run_cmd():
    proc = await asyncio.create_subprocess_shell(
        "echo hello",
        stdout=asyncio.subprocess.PIPE,
    )
    stdout, _ = await proc.communicate()
    return stdout.decode()

# --- Files ---
async def read_file():
    loop = asyncio.get_running_loop()
    with open("file.txt") as f:
        return await loop.run_in_executor(None, f.read)

# --- AnyIO (compatible asyncio+trio) ---
import anyio

async def anyio_main():
    async with anyio.create_task_group() as tg:
        tg.start_soon(worker, "A", 1)
        tg.start_soon(worker, "B", 2)

anyio.run(anyio_main)

3. Python Idioms

IDIOM 1 : Swap de variables

# 🚫 Mauvais
temp = a
a = b
b = temp

# ✅ Pythonic
a, b = b, a

IDIOM 2 : Test de vérité

# 🚫 Mauvais
if len(items) > 0: ...
if value == True: ...
if bool(x) == True: ...

# ✅ Pythonic
if items: ...
if value: ...
if x: ...

IDIOM 3 : Itération avec index

# 🚫 Mauvais
for i in range(len(items)):
    print(items[i])

# ✅ Pythonic
for i, item in enumerate(items):
    print(i, item)

IDIOM 4 : Itération sur deux listes

# 🚫 Mauvais
for i in range(len(names)):
    print(names[i], ages[i])

# ✅ Pythonic
for name, age in zip(names, ages):
    print(name, age)

IDIOM 5 : Construction de chaîne

# 🚫 Mauvais
result = ""
for s in strings:
    result += s

# ✅ Pythonic
result = "".join(strings)

IDIOM 6 : Valeur par défaut

# 🚫 Mauvais
if key in d:
    value = d[key]
else:
    value = default

# ✅ Pythonic
value = d.get(key, default)

IDIOM 7 : Vérification d'appartenance

# 🚫 Mauvais
if d.get(key) is not None: ...
if type(x) == int: ...

# ✅ Pythonic
if key in d: ...
if isinstance(x, int): ...

IDIOM 8 : Retour conditionnel

# 🚫 Mauvais
if condition:
    return True
else:
    return False

# ✅ Pythonic
return condition
# ou
return True if condition else False

IDIOM 9 : Filtrage de liste

# 🚫 Mauvais
result = []
for x in items:
    if x > 0:
        result.append(x)

# ✅ Pythonic
result = [x for x in items if x > 0]

IDIOM 10 : Ouverture de fichier

# 🚫 Mauvais
f = open("file.txt")
data = f.read()
f.close()

# ✅ Pythonic
with open("file.txt") as f:
    data = f.read()
# Le fichier est fermé automatiquement

IDIOM 11 : Opération ternaire

# ✅ Pythonic
status = "adult" if age >= 18 else "minor"

IDIOM 12 : Déballage de tuple

# ✅ Pythonic
name, age, city = ("Alice", 30, "Paris")
first, *middle, last = [1, 2, 3, 4, 5]
# first=1, middle=[2,3,4], last=5

IDIOM 13 : Négation d'appartenance

# ✅ Pythonic
if x not in items: ...
# au lieu de : if not x in items: ...

IDIOM 14 : Défense contre None

# ✅ Pythonic
result = maybe_none or default

IDIOM 15 : Itération avec condition

# ✅ Pythonic
all(x > 0 for x in items)   # True si tous > 0
any(x > 0 for x in items)   # True si un > 0

IDIOM 16 : Transposition matrice

# ✅ Pythonic
matrix = [[1, 2], [3, 4], [5, 6]]
transposed = list(zip(*matrix))
# [(1, 3, 5), (2, 4, 6)]

IDIOM 17 : Applatissement de liste

# ✅ Pythonic
nested = [[1, 2], [3, 4], [5]]
flat = [x for sub in nested for x in sub]
# [1, 2, 3, 4, 5]

IDIOM 18 : Comptage d'occurrences

# 🚫 Mauvais
counts = {}
for x in items:
    if x in counts:
        counts[x] += 1
    else:
        counts[x] = 1

# ✅ Pythonic (Python 3.10+)
from collections import Counter
counts = Counter(items)

IDIOM 19 : Dataclasses pour les données simples

# 🚫 Mauvais
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

# ✅ Pythonic
from dataclasses import dataclass
@dataclass
class Point:
    x: float
    y: float

IDIOM 20 : Walrus operator (:=)

# ✅ Pythonic (3.8+)
if (n := len(items)) > 10:
    print(f"Too many: {n}")

# vs
n = len(items)
if n > 10:
    print(f"Too many: {n}")

4. Common Mistakes

MISTAKE 1 : Utiliser is pour comparer des nombres

# 🚫 Mauvais
x = 256
y = 256
print(x is y)  # True (cache Python, mais pas garanti)

# ✅ Correct
print(x == y)  # True (toujours correct)

MISTAKE 2 : Mutables comme paramètres par défaut

# 🚫 Mauvais
def add_item(item, lst=[]):
    lst.append(item)
    return lst

# ✅ Correct
def add_item(item, lst=None):
    if lst is None:
        lst = []
    lst.append(item)
    return lst

MISTAKE 3 : Modifier une liste en l'itérant

# 🚫 Mauvais
for item in lst:
    if condition(item):
        lst.remove(item)  # Bug : saute des éléments

# ✅ Correct
lst = [item for item in lst if not condition(item)]

MISTAKE 4 : Confondre == et is pour les chaînes

# 🚫 Mauvais
if status is "ok": ...

# ✅ Correct
if status == "ok": ...

MISTAKE 5 : Ignorer les exceptions

# 🚫 Mauvais
try:
    risky_operation()
except Exception:  # Trop large
    pass

# ✅ Correct
try:
    risky_operation()
except SpecificError as e:
    logger.error("Opération échouée", exc_info=e)
    raise

MISTAKE 6 : Ne pas fermer les fichiers

# 🚫 Mauvais
f = open("file.txt")
data = f.read()
# f n'est pas fermé

# ✅ Correct
with open("file.txt") as f:
    data = f.read()

MISTAKE 7 : Shadowing de builtins

# 🚫 Mauvais
list = [1, 2, 3]  # Écrase la fonction builtin list()
id = 42           # Écrase la fonction id()

# ✅ Correct
my_list = [1, 2, 3]
item_id = 42

MISTAKE 8 : Type checking inutile

# 🚫 Mauvais
if type(x) == int: ...

# ✅ Correct (duck typing)
try:
    result = x + 1
except TypeError:
    pass

MISTAKE 9 : Chaînes de concaténation dans une boucle

# 🚫 Mauvais (O(n²))
result = ""
for s in many_strings:
    result += s

# ✅ Correct (O(n))
result = "".join(many_strings)

MISTAKE 10 : Ne pas utiliser enumerate

# 🚫 Mauvais
for i in range(len(items)):
    print(i, items[i])

# ✅ Correct
for i, item in enumerate(items):
    print(i, item)

MISTAKE 11 : Clé de dict mutable

# 🚫 Mauvais
d = {}
d[[1, 2]] = "value"  # TypeError: unhashable type: 'list'

# ✅ Correct
d[(1, 2)] = "value"

MISTAKE 12 : Référence circulaire

# 🚫 Mauvais
class A:
    def __init__(self):
        self.other = B(self)

class B:
    def __init__(self, a):
        self.other = a

# Cause des problèmes de garbage collection

MISTAKE 13 : Utiliser time.time() pour mesurer

# 🚫 Mauvais
start = time.time()
# ... opération ...
end = time.time()

# ✅ Correct
start = time.perf_counter()
# ... opération ...
end = time.perf_counter()

MISTAKE 14 : Comparer des floats avec ==

# 🚫 Mauvais
if 0.1 + 0.2 == 0.3: ...  # False !!

# ✅ Correct
if abs((0.1 + 0.2) - 0.3) < 1e-9: ...
# ou
import math
if math.isclose(0.1 + 0.2, 0.3): ...

MISTAKE 15 : Oublier return dans une fonction

# 🚫 Mauvais
def get_value():
    value = compute()  # Pas de return

result = get_value()  # None

# ✅ Correct
def get_value():
    return compute()

MISTAKE 16 : Modifier une boucle avec remove

# 🚫 Mauvais
items = [1, 2, 3, 4]
for item in items:
    if item % 2 == 0:
        items.remove(item)  # items = [1, 3, 4]

# ✅ Correct
items = [item for item in items if item % 2 != 0]

MISTAKE 17 : Ne pas utiliser __slots__ pour des millions d'objets

# 🚫 Mauvais
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
# Chaque instance → dict (coûteux)

# ✅ Correct
class Point:
    __slots__ = ('x', 'y')
    def __init__(self, x, y):
        self.x = x
        self.y = y
# Pas de dict, mémoire réduite

MISTAKE 18 : except: nu

# 🚫 Mauvais
try:
    something()
except:
    handle()

# ✅ Correct
try:
    something()
except Exception as e:
    handle(e)

MISTAKE 19 : Global mutable

# 🚫 Mauvais
MODULE_CACHE = {}

def process(data):
    MODULE_CACHE[data.id] = data
    # Effet de bord dangereux

# ✅ Correct (passer en paramètre)
def process(data, cache):
    cache[data.id] = data

MISTAKE 20 : Confusion entre extend et append

# 🚫 Mauvais
a = [1, 2]
a.append([3, 4])  # a = [1, 2, [3, 4]]

# ✅ Correct
a = [1, 2]
a.extend([3, 4])  # a = [1, 2, 3, 4]

MISTAKE 21 : Utiliser import *

# 🚫 Mauvais
from module import *
# Pollution du namespace, conflits potentiels

# ✅ Correct
from module import specific_function

MISTAKE 22 : Ne pas gérer les timezones

# 🚫 Mauvais
from datetime import datetime
now = datetime.now()  # Naive datetime

# ✅ Correct
from datetime import datetime, timezone
now = datetime.now(timezone.utc)

MISTAKE 23 : Multiplier les appels à len()

# 🚫 Mauvais
for i in range(len(items)):
    if i < len(items) - 1:
        process(items[i], items[i + 1])

# ✅ Correct
for a, b in zip(items, items[1:]):
    process(a, b)

MISTAKE 24 : Utiliser eval() ou exec()

# 🚫 Mauvais (injection possible)
eval(user_input)

# ✅ Correct
ast.literal_eval(user_input)  # Plus sûr

MISTAKE 25 : Oublier le self dans les méthodes

# 🚫 Mauvais
class MyClass:
    def method():
        print("Hello")

obj = MyClass()
obj.method()  # TypeError: method() takes 0 positional arguments but 1 was given

MISTAKE 26 : Copier une liste avec =

# 🚫 Mauvais
original = [1, 2, 3]
copy = original  # Référence, pas une copie

# ✅ Correct
copy = original.copy()
copy = list(original)
copy = original[:]

MISTAKE 27 : Sortie de contexte anticipée dans un générateur

# 🚫 Mauvais
def gen():
    with open("file.txt") as f:
        yield f.read()
    # Le fichier est fermé après le premier next()

MISTAKE 28 : Appeler super().__init__ dans l'ordre MRO

# 🚫 Mauvais
class C(A, B):
    def __init__(self):
        A.__init__(self)
        B.__init__(self)

# ✅ Correct
class C(A, B):
    def __init__(self):
        super().__init__()  # MRO automatique

MISTAKE 29 : Oublier await sur une coroutine

# 🚫 Mauvais
async def main():
    result = async_function()
    # Retourne une coroutine, pas le résultat

# ✅ Correct
async def main():
    result = await async_function()

MISTAKE 30 : Bloquer l'event loop

# 🚫 Mauvais
async def handler():
    time.sleep(1)  # Bloque tout l'event loop

# ✅ Correct
async def handler():
    await asyncio.sleep(1)  # Libère l'event loop

Prochain chapitre : 24-Tendances →
Les tendances actuelles et futures de l'écosystème Python.