Bienvenue en Python Backend
Module 01 · Bases du langage · ~2h
Python est un langage de programmation polyvalent, lisible et puissant, créé par Guido van Rossum en 1991. Sa philosophie tient en une phrase : "Il devrait y avoir une façon évidente de faire les choses." Aujourd'hui, Python est le langage le plus populaire au monde (index TIOBE 2024) et domine les domaines du backend web, de la data science, de l'IA et du scripting DevOps.
Qu'est-ce que Python ?
- Langage interprété — pas de compilation, exécution directe
- Typage dynamique et fort — les types sont réels mais inférés
- Syntaxe basée sur l'indentation — pas d'accolades
- Multi-paradigme : impératif, OOP, fonctionnel
- Batteries incluses : bibliothèque standard très riche
- Communauté massive : +450 000 packages sur PyPI
À quoi sert Python ?
- APIs REST — Flask, FastAPI, Django REST
- Automatisation — scripts, scraping, CI/CD
- Data Science — Pandas, NumPy, Matplotlib
- Intelligence Artificielle — TensorFlow, PyTorch
- DevOps — Ansible, AWS Lambda, scripts système
- Microservices — conteneurs, serverless
Pourquoi le maîtriser ?
- Salaire moyen dev Python : 45 000–65 000 €/an en France
- Utilisé par Netflix, Instagram, Spotify, Airbnb
- Apprendre plus vite : syntaxe 3× plus concise que Java
- Passerelle naturelle vers l'IA / ML
- Demande en forte croissance : +28% d'offres en 2 ans
- Très bon pour le prototypage rapide
💻 Python et le développement web — ce que ça change pour toi
🔗 Full-Stack complet
Tu maîtrises déjà le front (JS/HTML/CSS). Python t'ouvre le backend : écrire les APIs que ton front consomme, gérer la base de données, l'authentification.
⚡ APIs REST ultra-rapides
FastAPI génère automatiquement la doc OpenAPI, valide les données avec Pydantic, et rivalise avec Node.js en performance grâce à l'async ASGI.
🗄️ ORM puissant
SQLAlchemy te permet de manipuler PostgreSQL, MySQL, SQLite en Python pur. Fini les requêtes SQL brutes dangereuses — les relations et migrations sont automatisées.
🐳 Déploiement simplifié
Une image Docker Python slim, un docker-compose et ton API tourne partout — DigitalOcean, AWS, Render. Le même workflow que Node, mais avec l'écosystème Python.
🔄 Python vs JavaScript — comprendre les différences clés
| Aspect | Python | JavaScript |
|---|---|---|
| Syntaxe blocs | indentation (4 espaces) |
accolades { } |
| Null / vide | None |
null / undefined |
| Booléens | True / False |
true / false |
| Async | async def / await |
async function / await |
| Tableaux | list [1, 2, 3] |
Array [1, 2, 3] |
| Objets | dict {"clé": val} |
Object {clé: val} |
| Packages | pip + PyPI |
npm + npmjs.com |
🗺️ Ce que tu vas construire dans cette formation
1. Types de données
Python est un langage à typage dynamique mais fortement typé. Les variables n'ont pas de type déclaré, mais les valeurs oui.
| Type Python | Exemple | Équivalent JS | Remarque |
|---|---|---|---|
int | 42 | number | Précision illimitée |
float | 3.14 | number | 64 bits (IEEE 754) |
str | "hello" | string | Immuable, Unicode |
bool | True / False | boolean | Sous-classe de int |
None | None | null | Type NoneType |
bytes | b"data" | Uint8Array | Données binaires |
# ✅ Conversion de types
age = int("25") # str → int
prix = float("9.99") # str → float
texte = str(42) # int → str
valide = bool(1) # int → bool (True si != 0)
# Vérification de type
print(type(age)) # <class 'int'>
print(isinstance(age, int)) # True
print(isinstance(age, (int, float))) # True — union de types
# Valeurs "falsy" (évaluées à False)
# False, 0, 0.0, "", [], {}, (), None, set()
if not []:
print("Liste vide = False")
2. Collections
Python offre quatre types de collections fondamentaux, chacun avec ses caractéristiques propres.
# ── list : mutable, ordonnée, doublons autorisés
taches = ["todo", "in_progress", "done"]
taches.append("cancelled")
taches.remove("todo")
premier = taches[0]
derniers = taches[-2:] # slicing
# ── tuple : immuable, ordonnée
coords = (48.85, 2.35) # latitude, longitude Paris
lat, lon = coords # unpacking
# ── dict : clé-valeur, ordonné (Python 3.7+)
user = {
"id": 1,
"email": "alice@mail.fr",
"role": "admin"
}
role = user.get("role", "user") # valeur par défaut si absent
user["actif"] = True
for cle, val in user.items():
print(f"{cle}: {val}")
# ── set : non ordonné, sans doublons
statuts = {"todo", "done", "todo"} # {"todo", "done"}
# ✅ Compréhensions de liste
carres = [x**2 for x in range(1, 6)] # [1, 4, 9, 16, 25]
pairs = [x for x in range(10) if x % 2 == 0]
noms = {u["email"]: u["id"] for u in [user]} # dict comprehension
# ✅ Unpacking avancé
premier, *reste = [1, 2, 3, 4, 5] # premier=1, reste=[2,3,4,5]
a, b = b, a # swap sans variable temporaire
3. Fonctions
Les fonctions Python supportent les arguments positionnels, nommés, valeurs par défaut, et le typage avec annotations.
from typing import Optional
# ✅ Fonction typée avec valeur par défaut
def creer_tache(
titre: str,
statut: str = "todo",
priorite: str = "medium",
description: Optional[str] = None
) -> dict:
"""Crée un dictionnaire représentant une tâche."""
return {
"titre": titre,
"statut": statut,
"priorite": priorite,
"description": description
}
# Appels
t1 = creer_tache("Corriger le bug")
t2 = creer_tache("New feature", statut="in_progress", priorite="high")
# ── *args : positionnels variables en tuple
def additionner(*nombres: int) -> int:
return sum(nombres)
print(additionner(1, 2, 3, 4)) # 10
# ── **kwargs : nommés variables en dict
def log_evenement(niveau: str, **details) -> None:
print(f"[{niveau}]", details)
log_evenement("INFO", user_id=42, action="login")
# ── Fonctions comme objets (first-class)
def filtrer(items: list, predicat) -> list:
return [x for x in items if predicat(x)]
taches_urgentes = filtrer(taches_list, lambda t: t["priorite"] == "high")
# ── Générateur (économise la mémoire)
def paginer(items: list, taille: int = 10):
for debut in range(0, len(items), taille):
yield items[debut:debut + taille]
4. Modules, imports & environnement virtuel
Python utilise des environnements virtuels pour isoler les dépendances de chaque projet.
# ── Créer et activer un venv
python -m venv venv
# Windows
venv\Scripts\activate
# macOS / Linux
source venv/bin/activate
# Installer des dépendances
pip install flask fastapi sqlalchemy
# Sauvegarder les dépendances
pip freeze > requirements.txt
# Réinstaller
pip install -r requirements.txt
# ── Imports
import os # module standard
import json # module standard
from pathlib import Path # import sélectif
from typing import Optional, Union, List, Dict
# ── __main__ : exécution directe uniquement
def main() -> None:
print("Démarrage de l'application")
if __name__ == "__main__":
main()
# ── Module propre
# Chaque fichier .py est un module.
# Un dossier avec __init__.py est un package.
# from mon_package.mon_module import ma_fonction
5. Gestion d'erreurs
Python utilise des exceptions pour gérer les erreurs. Il vaut mieux être précis sur les types d'exceptions capturées.
# ✅ Hiérarchie des exceptions courantes
# Exception
# ├── ValueError (valeur incorrecte)
# ├── TypeError (mauvais type)
# ├── KeyError (clé dict absente)
# ├── IndexError (index hors limites)
# ├── FileNotFoundError (fichier absent)
# └── AttributeError (attribut inexistant)
# ✅ Bloc try/except/else/finally
def lire_config(chemin: str) -> dict:
try:
with open(chemin, "r", encoding="utf-8") as f:
return json.load(f)
except FileNotFoundError:
print(f"Fichier non trouvé : {chemin}")
return {}
except json.JSONDecodeError as e:
raise ValueError(f"JSON invalide dans {chemin}: {e}") from e
else:
# S'exécute seulement si aucune exception
print("Config chargée avec succès")
finally:
# S'exécute toujours
print("Lecture terminée")
# ✅ Exception personnalisée
class TaskNotFoundError(Exception):
def __init__(self, task_id: int):
super().__init__(f"Tâche #{task_id} introuvable")
self.task_id = task_id
def get_task(task_id: int) -> dict:
# Simulation
raise TaskNotFoundError(task_id)
# ✅ Utilisation
try:
get_task(999)
except TaskNotFoundError as e:
print(e) # "Tâche #999 introuvable"
print(e.task_id) # 999
6. POO Python
Python supporte la POO complète avec héritage, propriétés, méthodes statiques et de classe. Le décorateur @dataclass simplifie la définition de classes de données.
from dataclasses import dataclass, field
from datetime import datetime
from typing import Optional
# ✅ @dataclass : génère __init__, __repr__, __eq__
@dataclass
class Task:
id: int
titre: str
statut: str = "todo"
priorite: str = "medium"
created_at: datetime = field(default_factory=datetime.now)
tags: list = field(default_factory=list)
def est_terminee(self) -> bool:
return self.statut == "done"
def __str__(self) -> str:
return f"[{self.priorite.upper()}] {self.titre} ({self.statut})"
# ✅ Classe classique avec @property
class Project:
def __init__(self, titre: str, owner_id: int):
self._titre = titre
self.owner_id = owner_id
self._taches: list[Task] = []
@property
def titre(self) -> str:
return self._titre
@titre.setter
def titre(self, valeur: str) -> None:
if not valeur.strip():
raise ValueError("Le titre ne peut pas être vide")
self._titre = valeur.strip()
@property
def nb_taches(self) -> int:
return len(self._taches)
def ajouter_tache(self, tache: Task) -> None:
self._taches.append(tache)
@staticmethod
def valider_titre(titre: str) -> bool:
return len(titre.strip()) >= 3
@classmethod
def creer_vide(cls, owner_id: int) -> "Project":
return cls("Nouveau projet", owner_id)
# ✅ Héritage
class UrgentTask(Task):
def __init__(self, titre: str, **kwargs):
super().__init__(titre=titre, priorite="critical", **kwargs)
def notifier(self) -> str:
return f"URGENT: {self.titre}"
7. Fichiers & JSON
La lecture/écriture de fichiers avec with garantit la fermeture automatique, même en cas d'exception.
import json
from pathlib import Path
from dataclasses import asdict
# ── Lecture d'un fichier texte
def lire_fichier(chemin: str) -> str:
with open(chemin, "r", encoding="utf-8") as f:
return f.read()
# ── Écriture JSON
def sauvegarder_taches(taches: list[Task], fichier: str) -> None:
data = [asdict(t) for t in taches]
# asdict() convertit datetime en str automatiquement
with open(fichier, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2, default=str)
# ── Lecture JSON
def charger_taches(fichier: str) -> list[dict]:
chemin = Path(fichier)
if not chemin.exists():
return []
with chemin.open("r", encoding="utf-8") as f:
return json.load(f)
# ── pathlib : manipulation de chemins
base = Path(__file__).parent # dossier du script
data_dir = base / "data"
data_dir.mkdir(parents=True, exist_ok=True)
tasks_file = data_dir / "tasks.json"
# Lister les fichiers
for fichier in base.glob("*.py"):
print(fichier.name)
8. Typage statique (typing)
Python 3.10+ simplifie les annotations avec la syntaxe |. Les outils comme mypy vérifient les types sans exécuter le code.
from typing import TypeAlias, TypeVar, Generic
# ── Python 3.10+ : syntaxe moderne
def get_user(user_id: int) -> dict | None:
...
def process(value: int | float | str) -> str:
return str(value)
# ── TypeAlias : nommer un type complexe
UserId: TypeAlias = int
TaskDict: TypeAlias = dict[str, str | int | None]
# ── TypeVar + Generic : types génériques
T = TypeVar("T")
class PaginatedResponse(Generic[T]):
def __init__(self, data: list[T], total: int, page: int, pages: int):
self.data = data
self.total = total
self.page = page
self.pages = pages
# Utilisation
resp: PaginatedResponse[Task] = PaginatedResponse(
data=[Task(1, "Fix bug")],
total=1, page=1, pages=1
)
# ── Annotations pratiques
from typing import Callable, Iterator, Any
FilterFunc: TypeAlias = Callable[[Task], bool]
def filtrer_taches(
taches: list[Task],
filtre: FilterFunc
) -> Iterator[Task]:
return filter(filtre, taches)
# ── Vérification statique avec mypy
# pip install mypy
# mypy mon_fichier.py