Coder
proprement
Conventions PEP 8, clean code, principes SOLID, type hints et outils de qualité.
Conventions de nommage
# Noms non descriptifs
def f(x, y):
return x + y
l = [1, 2, 3]
N = 100
class gestion_utilisateur:
pass
# Noms explicites
def additionner(a: int, b: int) -> int:
return a + b
nombres = [1, 2, 3]
MAX_UTILISATEURS = 100
class GestionUtilisateur:
pass
Formatage & indentation
# Espaces incohérents
x=1+2
y = x *3
# Pas d'espace après virgule
lst = [1,2,3]
# Parenthèses superflues
if (x > 0):
return (True)
# Comparaison à True/None
if actif == True:
pass
if val == None:
pass
# Espaces autour des opérateurs
x = 1 + 2
y = x * 3
# Espace après virgule
lst = [1, 2, 3]
# Pas de parenthèses inutiles
if x > 0:
return True
# Comparaison idiomatique
if actif:
pass
if val is None:
pass
Longueur de ligne
PEP 8 recommande 79 caractères max. En pratique, 88-100 est acceptable (Black utilise 88).
résultat = une_fonction_longue(argument_un, argument_deux, argument_trois, argument_quatre)
résultat = une_fonction_longue(
argument_un,
argument_deux,
argument_trois,
argument_quatre, # virgule finale ok
)
Commentaires & Docstrings
Un bon code se lit sans commentaires. Commenter le pourquoi, pas le quoi. # incrémenter i de 1 est inutile. # cas limite : liste vide retourne None selon spec est utile.
def calculer_moyenne(valeurs: list[float]) -> float:
"""Calcule la moyenne arithmétique d'une liste de valeurs.
Args:
valeurs: Liste de nombres réels. Ne doit pas être vide.
Returns:
La moyenne des valeurs.
Raises:
ValueError: Si la liste est vide.
Example:
>>> calculer_moyenne([1.0, 2.0, 3.0])
2.0
"""
if not valeurs:
raise ValueError("La liste ne peut pas être vide")
return sum(valeurs) / len(valeurs)
Fonctions propres
calculer_total(), valider_email(), charger_config().def traiter_utilisateur(données):
# valide, sauvegarde ET envoie email
if not données["email"]:
raise ValueError()
bd.sauvegarder(données)
email.envoyer(données["email"])
def valider_utilisateur(données): ...
def sauvegarder_utilisateur(données): ...
def notifier_utilisateur(email): ...
Principes SOLID
Single Responsibility
Une classe = une seule raison de changer. Rapport ne devrait pas gérer à la fois le calcul et l'impression.
Open / Closed
Ouvert à l'extension, fermé à la modification. Ajouter une fonctionnalité = créer une sous-classe, pas modifier le code existant.
Liskov Substitution
Une sous-classe doit pouvoir remplacer sa classe parente sans casser le programme. Si Carré hérite de Rectangle mais casse le comportement, c'est un problème.
Interface Segregation
Plusieurs petites interfaces valent mieux qu'une grosse. Ne pas forcer une classe à implémenter des méthodes dont elle n'a pas besoin.
Dependency Inversion
Dépendre d'abstractions, pas de classes concrètes. Injecter les dépendances plutôt que les instancier dans la classe.
# ✗ Dépendance concrète
class Service:
def __init__(self):
self.bd = MySQL() # couplé !
# ✓ Injection de dépendance
class Service:
def __init__(self, bd: BaseDeDonnées):
self.bd = bd
DRY & KISS
Don't Repeat Yourself
Toute connaissance doit avoir une représentation unique dans le système. Si vous copiez-collez du code, c'est un signal à extraire une fonction.
prix_ht = 100
tva_1 = prix_ht * 0.14975
prix_ht_2 = 200
tva_2 = prix_ht_2 * 0.14975
TVA = 0.14975
def calculer_tva(prix_ht):
return prix_ht * TVA
Keep It Simple, Stupid
La solution la plus simple qui fonctionne est généralement la meilleure. Éviter la sur-ingénierie.
class StringReverserFactory:
def create_reverser(self):
return StringReverser()
class StringReverser:
def reverse(self, s): return s[::-1]
def inverser_chaîne(s: str) -> str:
return s[::-1]
Type hints
Facultatifs mais très recommandés. Améliorent la lisibilité, permettent l'autocomplétion IDE, et détectent les erreurs avec mypy.
# Types de base
def saluer(nom: str, fois: int = 1) -> str:
return nom * fois
# Types composés (Python 3.9+ : list, dict directement)
def moyennes(scores: list[float]) -> dict[str, float]:
return {"moy": sum(scores) / len(scores)}
# Optionnel (valeur ou None)
def trouver(lst: list[int], cible: int) -> int | None:
try: return lst.index(cible)
except ValueError: return None
# Callable
from collections.abc import Callable
def appliquer(f: Callable[[int], int], x: int) -> int:
return f(x)
# Dataclass — structure typée légère
from dataclasses import dataclass
@dataclass
class Point:
x: float
y: float
label: str = "" # valeur par défaut
p = Point(1.0, 2.0)
p.x, p.y # accès direct
Linters & formatters
| Outil | Rôle | Commande |
|---|---|---|
black | Formateur automatique (opinionné, PEP 8) | black . |
flake8 | Linter — détecte les violations PEP 8 | flake8 . |
pylint | Analyse statique avancée | pylint mon_module/ |
mypy | Vérification des types statiques | mypy . |
isort | Trier les imports automatiquement | isort . |
pre-commit | Exécuter les outils avant chaque commit | pre-commit install |
Configurer black + flake8 + mypy dans VS Code pour avoir le retour en temps réel. Ajouter pre-commit pour s'assurer que le code commité respecte toujours les standards.
Cheat sheet
Nommage PEP 8
| Variable / fonction | snake_case |
| Classe | PascalCase |
| Constante | SCREAMING_SNAKE |
| Privé | _underscore |
| Module | minuscules |
SOLID en un mot
| S | Une responsabilité |
| O | Étendre, pas modifier |
| L | Substituabilité |
| I | Interfaces petites |
| D | Injecter les dépendances |
Principes généraux
| DRY | Ne pas se répéter |
| KISS | Garder simple |
| YAGNI | Ne pas anticiper |
| Commenter | Le pourquoi, pas le quoi |
| Docstring | Args / Returns / Raises |