Debutant 21 min de lecture · 4 451 mots

Python pour débutants : Syntaxe de base et structures de données

Introduction

Python est l’un des langages de programmation les plus accessibles et puissants pour débuter dans le développement. Sa syntaxe claire et ses structures de données intégrées en font un choix idéal pour apprendre la programmation tout en construisant des applications réelles.

Dans cet article, vous découvrirez les fondamentaux de Python 3.10+ avec des exemples concrets, des bonnes pratiques, et des projets pratiques pour consolider vos connaissances.

Configuration de l’environnement virtuel

Avant de commencer, créons un environnement de développement isolé et professionnel.

Installation et création de l’environnement

# Vérifier la version de Python (3.10 ou supérieur requis)
python3 --version

# Créer un environnement virtuel
python3 -m venv venv

# Activer l'environnement virtuel
# Sur Linux/Mac:
source venv/bin/activate

# Sur Windows:
# venvScriptsactivate

# Mettre à jour pip
pip install --upgrade pip

Fichier requirements.txt

# requirements.txt - Dépendances pour le développement
pytest==7.4.3
pytest-cov==4.1.0
black==23.12.1
mypy==1.7.1
pylint==3.0.3
ipython==8.18.1

Installation des dépendances:

pip install -r requirements.txt

Syntaxe de base et types de données

Variables et types primitifs

"""
basic_types.py - Démonstration des types de base en Python
"""

from typing import Final

# Constantes (convention: MAJUSCULES)
MAX_USERS: Final[int] = 100
PI: Final[float] = 3.14159
APP_NAME: Final[str] = "MonApplication"

# Variables avec type hints
age: int = 25
prix: float = 19.99
nom: str = "Alice"
est_actif: bool = True

# Inférence de type (Python devine le type)
ville = "Paris"  # Python sait que c'est une str
compteur = 0     # Python sait que c'est un int

def afficher_informations(nom: str, age: int, ville: str) -> None:
    """
    Affiche les informations d'un utilisateur.

    Args:
        nom: Le nom de l'utilisateur
        age: L'âge de l'utilisateur
        ville: La ville de résidence
    """
    print(f"Nom: {nom}")
    print(f"Age: {age} ans")
    print(f"Ville: {ville}")
    print(f"Application: {APP_NAME}")

if __name__ == "__main__":
    afficher_informations(nom, age, ville)

Opérateurs et expressions

"""
operators.py - Opérateurs arithmétiques, logiques et de comparaison
"""

from typing import Union

def calculatrice_basique(
    a: Union[int, float],
    b: Union[int, float],
    operation: str
) -> Union[int, float]:
    """
    Effectue une opération arithmétique de base.

    Args:
        a: Premier nombre
        b: Deuxième nombre
        operation: Type d'opération (+, -, *, /, //, %, **)

    Returns:
        Le résultat de l'opération

    Raises:
        ValueError: Si l'opération n'est pas reconnue
        ZeroDivisionError: Si division par zéro
    """
    operations = {
        "+": a + b,           # Addition
        "-": a - b,           # Soustraction
        "*": a * b,           # Multiplication
        "/": a / b,           # Division
        "//": a // b,         # Division entière
        "%": a % b,           # Modulo (reste)
        "**": a ** b,         # Puissance
    }

    if operation not in operations:
        raise ValueError(f"Opération '{operation}' non reconnue")

    return operations[operation]

# Opérateurs de comparaison
def est_dans_intervalle(valeur: float, min_val: float, max_val: float) -> bool:
    """Vérifie si une valeur est dans un intervalle."""
    return min_val <= valeur <= max_val

# Opérateurs logiques
def peut_voter(age: int, nationalite: str, casier_judiciaire: bool) -> bool:
    """
    Détermine si une personne peut voter.

    Args:
        age: L'âge de la personne
        nationalite: La nationalité
        casier_judiciaire: True si casier judiciaire

    Returns:
        True si la personne peut voter
    """
    return (
        age >= 18
        and nationalite == "française"
        and not casier_judiciaire
    )

if __name__ == "__main__":
    # Tests des opérateurs
    print(f"10 + 5 = {calculatrice_basique(10, 5, '+')}")
    print(f"10 / 3 = {calculatrice_basique(10, 3, '/')}")
    print(f"10 // 3 = {calculatrice_basique(10, 3, '//')}")
    print(f"10 % 3 = {calculatrice_basique(10, 3, '%')}")
    print(f"2 ** 8 = {calculatrice_basique(2, 8, '**')}")

    print(f"n15 est entre 10 et 20: {est_dans_intervalle(15, 10, 20)}")
    print(f"Peut voter (25 ans, française, pas de casier): {peut_voter(25, 'française', False)}")

Structures de données intégrées

Listes (Lists)

"""
lists_operations.py - Opérations avancées sur les listes
"""

from typing import List, Optional, Any

def gestion_taches() -> None:
    """Démonstration complète des opérations sur les listes."""

    # Création de listes
    taches: List[str] = ["Apprendre Python", "Faire les courses", "Répondre aux emails"]
    nombres: List[int] = [1, 2, 3, 4, 5]
    mixte: List[Any] = [1, "texte", 3.14, True]  # Type mixte (éviter en production)

    # Ajout d'éléments
    taches.append("Appeler le client")  # Ajoute à la fin
    taches.insert(0, "Vérifier les emails")  # Insère à l'index 0
    taches.extend(["Réunion d'équipe", "Code review"])  # Ajoute plusieurs éléments

    # Suppression d'éléments
    taches.remove("Faire les courses")  # Supprime par valeur
    element_supprime = taches.pop()  # Supprime et retourne le dernier élément
    del taches[0]  # Supprime par index

    # Accès et modification
    premiere_tache = taches[0]
    derniere_tache = taches[-1]
    taches[1] = "Nouvelle tâche"

    # Slicing (découpage)
    deux_premieres = taches[:2]
    deux_dernieres = taches[-2:]
    milieu = taches[1:3]

    # List comprehension (compréhension de liste)
    nombres_carres = [n ** 2 for n in nombres]
    nombres_pairs = [n for n in nombres if n % 2 == 0]

    print("Tâches actuelles:", taches)
    print("Carrés:", nombres_carres)
    print("Nombres pairs:", nombres_pairs)

def filtrer_et_transformer(
    nombres: List[int],
    seuil: int
) -> List[int]:
    """
    Filtre et transforme une liste de nombres.

    Args:
        nombres: Liste de nombres à traiter
        seuil: Valeur minimale pour le filtre

    Returns:
        Liste des nombres supérieurs au seuil, multipliés par 2
    """
    return [n * 2 for n in nombres if n > seuil]

def trouver_element(
    liste: List[Any],
    element: Any
) -> Optional[int]:
    """
    Trouve l'index d'un élément dans une liste.

    Args:
        liste: La liste à parcourir
        element: L'élément à chercher

    Returns:
        L'index de l'élément ou None si non trouvé
    """
    try:
        return liste.index(element)
    except ValueError:
        return None

if __name__ == "__main__":
    gestion_taches()

    nombres = [1, 5, 10, 15, 20, 25]
    resultat = filtrer_et_transformer(nombres, 10)
    print(f"nNombres > 10, multipliés par 2: {resultat}")

    index = trouver_element(nombres, 15)
    print(f"Index de 15: {index}")

Dictionnaires (Dictionaries)

"""
dictionaries_operations.py - Gestion avancée des dictionnaires
"""

from typing import Dict, List, Optional, Any

def gestion_utilisateurs() -> None:
    """Démonstration des opérations sur les dictionnaires."""

    # Création de dictionnaires
    utilisateur: Dict[str, Any] = {
        "id": 1,
        "nom": "Dupont",
        "prenom": "Marie",
        "age": 28,
        "email": "marie.dupont@example.com",
        "roles": ["user", "moderator"]
    }

    # Accès aux valeurs
    nom = utilisateur["nom"]
    age = utilisateur.get("age")  # Plus sûr (retourne None si absent)
    telephone = utilisateur.get("telephone", "Non renseigné")  # Valeur par défaut

    # Modification et ajout
    utilisateur["age"] = 29
    utilisateur["ville"] = "Paris"

    # Suppression
    del utilisateur["roles"]
    email_supprime = utilisateur.pop("email")

    # Itération
    for cle, valeur in utilisateur.items():
        print(f"{cle}: {valeur}")

    # Clés et valeurs
    toutes_les_cles = list(utilisateur.keys())
    toutes_les_valeurs = list(utilisateur.values())

    print(f"nClés: {toutes_les_cles}")
    print(f"Valeurs: {toutes_les_valeurs}")

class GestionnaireUtilisateurs:
    """Gestionnaire d'utilisateurs avec dictionnaire."""

    def __init__(self) -> None:
        """Initialise le gestionnaire."""
        self.utilisateurs: Dict[int, Dict[str, Any]] = {}
        self._prochain_id: int = 1

    def ajouter_utilisateur(
        self,
        nom: str,
        prenom: str,
        email: str,
        age: int
    ) -> int:
        """
        Ajoute un nouvel utilisateur.

        Args:
            nom: Nom de famille
            prenom: Prénom
            email: Adresse email
            age: Age

        Returns:
            L'ID de l'utilisateur créé
        """
        utilisateur_id = self._prochain_id
        self.utilisateurs[utilisateur_id] = {
            "nom": nom,
            "prenom": prenom,
            "email": email,
            "age": age
        }
        self._prochain_id += 1
        return utilisateur_id

    def obtenir_utilisateur(self, user_id: int) -> Optional[Dict[str, Any]]:
        """Récupère un utilisateur par son ID."""
        return self.utilisateurs.get(user_id)

    def rechercher_par_age(self, age_min: int, age_max: int) -> List[Dict[str, Any]]:
        """
        Recherche des utilisateurs par tranche d'âge.

        Args:
            age_min: Age minimum
            age_max: Age maximum

        Returns:
            Liste des utilisateurs correspondants
        """
        return [
            {**user, "id": uid}
            for uid, user in self.utilisateurs.items()
            if age_min <= user["age"] <= age_max
        ]

    def statistiques(self) -> Dict[str, Any]:
        """Calcule des statistiques sur les utilisateurs."""
        if not self.utilisateurs:
            return {"total": 0, "age_moyen": 0}

        ages = [user["age"] for user in self.utilisateurs.values()]
        return {
            "total": len(self.utilisateurs),
            "age_moyen": sum(ages) / len(ages),
            "age_min": min(ages),
            "age_max": max(ages)
        }

if __name__ == "__main__":
    gestion_utilisateurs()

    print("n" + "="*50)
    print("Gestionnaire d'utilisateurs")
    print("="*50 + "n")

    gestionnaire = GestionnaireUtilisateurs()

    # Ajout d'utilisateurs
    id1 = gestionnaire.ajouter_utilisateur("Dupont", "Marie", "marie@example.com", 28)
    id2 = gestionnaire.ajouter_utilisateur("Martin", "Pierre", "pierre@example.com", 35)
    id3 = gestionnaire.ajouter_utilisateur("Durand", "Sophie", "sophie@example.com", 22)

    # Recherche
    jeunes = gestionnaire.rechercher_par_age(20, 30)
    print(f"Utilisateurs entre 20 et 30 ans: {jeunes}")

    # Statistiques
    stats = gestionnaire.statistiques()
    print(f"nStatistiques: {stats}")

Tuples et Sets

"""
tuples_sets.py - Tuples et ensembles
"""

from typing import Tuple, Set, FrozenSet, List

# TUPLES - Immuables, ordonnés
def coordonnees_gps() -> Tuple[float, float]:
    """
    Retourne des coordonnées GPS (latitude, longitude).

    Returns:
        Un tuple de coordonnées
    """
    return 48.8566, 2.3522  # Paris

def informations_personne() -> Tuple[str, int, str]:
    """Retourne des informations immuables sur une personne."""
    return "Dupont", 28, "Paris"

# Named tuples pour plus de lisibilité
from typing import NamedTuple

class Coordonnees(NamedTuple):
    """Coordonnées GPS avec noms explicites."""
    latitude: float
    longitude: float
    altitude: float = 0.0  # Valeur par défaut

class Personne(NamedTuple):
    """Informations d'une personne."""
    nom: str
    age: int
    ville: str
    email: str = ""

# SETS - Ensembles sans doublons
class GestionEnsembles:
    """Gestion d'ensembles pour opérations mathématiques."""

    @staticmethod
    def supprimer_doublons(elements: List[str]) -> List[str]:
        """
        Supprime les doublons d'une liste en préservant l'ordre.

        Args:
            elements: Liste avec potentiellement des doublons

        Returns:
            Liste sans doublons
        """
        # Méthode 1: avec set (ne préserve pas l'ordre)
        # return list(set(elements))

        # Méthode 2: préserve l'ordre
        vu: Set[str] = set()
        resultat: List[str] = []
        for element in elements:
            if element not in vu:
                vu.add(element)
                resultat.append(element)
        return resultat

    @staticmethod
    def operations_ensembles() -> None:
        """Démonstration des opérations sur les ensembles."""

        # Création
        fruits1: Set[str] = {"pomme", "banane", "orange"}
        fruits2: Set[str] = {"banane", "kiwi", "mangue"}

        # Union (tous les éléments)
        tous_fruits = fruits1 | fruits2
        # ou: tous_fruits = fruits1.union(fruits2)

        # Intersection (éléments communs)
        fruits_communs = fruits1 & fruits2
        # ou: fruits_communs = fruits1.intersection(fruits2)

        # Différence (dans fruits1 mais pas dans fruits2)
        uniquement_fruits1 = fruits1 - fruits2
        # ou: uniquement_fruits1 = fruits1.difference(fruits2)

        # Différence symétrique (dans l'un ou l'autre, mais pas les deux)
        fruits_differents = fruits1 ^ fruits2
        # ou: fruits_differents = fruits1.symmetric_difference(fruits2)

        print("Fruits1:", fruits1)
        print("Fruits2:", fruits2)
        print("Union:", tous_fruits)
        print("Intersection:", fruits_communs)
        print("Différence:", uniquement_fruits1)
        print("Différence symétrique:", fruits_differents)

    @staticmethod
    def tags_articles() -> None:
        """Exemple pratique: gestion de tags d'articles."""

        article1_tags: Set[str] = {"python", "programmation", "debutant"}
        article2_tags: Set[str] = {"python", "web", "django"}
        article3_tags: Set[str] = {"javascript", "web", "react"}

        # Trouver les tags communs à tous les articles Python
        tags_python = article1_tags & article2_tags
        print(f"nTags communs aux articles Python: {tags_python}")

        # Tous les tags uniques
        tous_tags = article1_tags | article2_tags | article3_tags
        print(f"Tous les tags: {tous_tags}")

        # Tags spécifiques à Python
        tags_specifiques_python = (article1_tags | article2_tags) - article3_tags
        print(f"Tags spécifiques Python: {tags_specifiques_python}")

# FrozenSet - Ensemble immuable (peut être clé de dictionnaire)
def creer_permissions() -> FrozenSet[str]:
    """Crée un ensemble immuable de permissions."""
    return frozenset(["lire", "ecrire", "executer"])

if __name__ == "__main__":
    # Tuples
    lat, lon = coordonnees_gps()
    print(f"Coordonnées Paris: {lat}, {lon}")

    # Named tuples
    paris = Coordonnees(48.8566, 2.3522, 35.0)
    print(f"Paris - Latitude: {paris.latitude}, Longitude: {paris.longitude}")

    personne = Personne("Dupont", 28, "Paris", "dupont@example.com")
    print(f"Personne: {personne.nom}, {personne.age} ans")

    # Sets
    print("n" + "="*50)
    liste_avec_doublons = ["a", "b", "c", "a", "b", "d"]
    sans_doublons = GestionEnsembles.supprimer_doublons(liste_avec_doublons)
    print(f"Liste originale: {liste_avec_doublons}")
    print(f"Sans doublons: {sans_doublons}")

    print("n" + "="*50)
    GestionEnsembles.operations_ensembles()
    GestionEnsembles.tags_articles()

    # FrozenSet
    perms = creer_permissions()
    print(f"nPermissions: {perms}")

Structures de contrôle

Conditions et boucles

"""
control_structures.py - Structures de contrôle avancées
"""

from typing import List, Dict, Optional, Any
from enum import Enum

class NiveauUtilisateur(Enum):
    """Énumération des niveaux d'utilisateur."""
    INVITE = "invite"
    UTILISATEUR = "utilisateur"
    MODERATEUR = "moderateur"
    ADMINISTRATEUR = "administrateur"

def verifier_acces(
    niveau: NiveauUtilisateur,
    age: int,
    est_verifie: bool
) -> str:
    """
    Vérifie les droits d'accès d'un utilisateur.

    Args:
        niveau: Niveau de l'utilisateur
        age: Age de l'utilisateur
        est_verifie: Si le compte est vérifié

    Returns:
        Message de statut d'accès
    """
    # Conditions multiples avec elif
    if niveau == NiveauUtilisateur.ADMINISTRATEUR:
        return "Accès complet accordé"
    elif niveau == NiveauUtilisateur.MODERATEUR and est_verifie:
        return "Accès modérateur accordé"
    elif niveau == NiveauUtilisateur.UTILISATEUR and age >= 18 and est_verifie:
        return "Accès utilisateur accordé"
    elif niveau == NiveauUtilisateur.UTILISATEUR and age < 18:
        return "Accès utilisateur limité (mineur)"
    elif not est_verifie:
        return "Vérification du compte requise"
    else:
        return "Accès invité limité"

def calculer_remise(montant: float, code_client: str) -> float:
    """
    Calcule une remise selon le type de client.

    Args:
        montant: Montant de l'achat
        code_client: Code du type de client

    Returns:
        Montant de la remise
    """
    # Pattern matching (Python 3.10+)
    match code_client:
        case "VIP":
            return montant * 0.20
        case "PREMIUM":
            return montant * 0.15
        case "STANDARD":
            return montant * 0.10
        case "NOUVEAU":
            return montant * 0.05 if montant > 100 else 0
        case *:  # Cas par défaut
            return 0

def traiter_commandes(commandes: List[Dict[str, Any]]) -> None:
    """
    Traite une liste de commandes avec différentes boucles.

    Args:
        commandes: Liste des commandes à traiter
    """
    print("=== Traitement des commandes ===n")

    # Boucle for classique
    for i, commande in enumerate(commandes, start=1):
        print(f"Commande #{i}:")
        print(f"  Client: {commande['client']}")
        print(f"  Montant: {commande['montant']}€")

        # Continue: passe à l'itération suivante
        if commande['montant'] < 10:
            print("  -> Montant trop faible, ignorée")
            continue

        # Break: sort de la boucle
        if commande.get('annulee', False):
            print("  -> Commande annulée, arrêt du traitement")
            break

        print("  -> Traitée avec succès")

    print("n=== Analyse des montants ===n")

    # Boucle while
    total = 0
    index = 0
    while index < len(commandes) and not commandes[index].get('annulee', False):
        total += commandes[index]['montant']
        index += 1

    print(f"Total des commandes traitées: {total}€")
    print(f"Nombre de commandes: {index}")

def filtrer_et_transformer_donnees(
    donnees: List[int],
    seuil: int
) -> Dict[str, List[int]]:
    """
    Filtre et catégorise des données.

    Args:
        donnees: Liste de nombres à traiter
        seuil: Valeur de référence

    Returns:
        Dictionnaire avec les données catégorisées
    """
    petits: List[int] = []
    moyens: List[int] = []
    grands: List[int] = []

    for valeur in donnees:
        if valeur < seuil:
            petits.append(valeur)
        elif valeur < seuil * 2:
            moyens.append(valeur)
        else:
            grands.append(valeur)

    return {
        "petits": petits,
        "moyens": moyens,
        "grands": grands
    }

# Compréhensions avancées
def comprehensions_avancees() -> None:
    """Démonstration de compréhensions avancées."""

    # List comprehension avec condition
    nombres = list(range(1, 21))
    pairs = [n for n in nombres if n % 2 == 0]
    carres_impairs = [n__2 for n in nombres if n % 2 != 0]

    # Dict comprehension
    carres_dict = {n: n__2 for n in range(1, 11)}
    pairs_dict = {n: n__2 for n in range(1, 11) if n % 2 == 0}

    # Set comprehension
    chiffres_uniques = {n % 10 for n in range(100)}

    # Nested comprehension (compréhension imbriquée)
    matrice = [[i_j for j in range(1, 6)] for i in range(1, 6)]

    # List comprehension avec if-else
    categories = ["pair" if n % 2 == 0 else "impair" for n in range(1, 11)]

    print("Nombres pairs:", pairs)
    print("Carrés des impairs:", carres_impairs)
    print("Dictionnaire carrés:", carres_dict)
    print("Table de multiplication (5x5):")
    for ligne in matrice:
        print(ligne)
    print("Catégories:", categories)

if __name__ == "__main__":
    # Test vérification d'accès
    print(verifier_acces(NiveauUtilisateur.UTILISATEUR, 25, True))
    print(verifier_acces(NiveauUtilisateur.UTILISATEUR, 16, True))
    print(verifier_acces(NiveauUtilisateur.MODERATEUR, 30, False))

    print("n" + "="*50 + "n")

    # Test calcul de remise
    print(f"Remise VIP sur 200€: {calculer_remise(200, 'VIP')}€")
    print(f"Remise NOUVEAU sur 50€: {calculer_remise(50, 'NOUVEAU')}€")
    print(f"Remise NOUVEAU sur 150€: {calculer_remise(150, 'NOUVEAU')}€")

    print("n" + "="*50 + "n")

    # Test traitement de commandes
    commandes = [
        {"client": "Alice", "montant": 150.00},
        {"client": "Bob", "montant": 5.00},
        {"client": "Charlie", "montant": 75.50},
        {"client": "David", "montant": 200.00, "annulee": True},
        {"client": "Eve", "montant": 50.00},
    ]
    traiter_commandes(commandes)

    print("n" + "="*50 + "n")

    # Test filtrage de données
    donnees = [5, 15, 25, 35, 45, 55, 8, 12, 18]
    resultat = filtrer_et_transformer_donnees(donnees, 20)
    print("Données filtrées:")
    for categorie, valeurs in resultat.items():
        print(f"  {categorie}: {valeurs}")

    print("n" + "="*50 + "n")

    comprehensions_avancees()

Fonctions et portée des variables

"""
functions_scope.py - Fonctions avancées et portée des variables
"""

from typing import List, Optional, Callable, Any
from functools import wraps
import time

# Variables globales (à éviter en production)
TAUX_TVA: float = 0.20

def calculer_prix_ttc(prix_ht: float, taux: Optional[float] = None) -> float:
    """
    Calcule le prix TTC.

    Args:
        prix_ht: Prix hors taxes
        taux: Taux de TVA personnalisé (utilise TAUX_TVA si None)

    Returns:
        Prix toutes taxes comprises
    """
    taux_utilise = taux if taux is not None else TAUX_TVA
    return prix_ht * (1 + taux_utilise)

# Arguments par défaut, *args et **kwargs
def creer_utilisateur(
    nom: str,
    prenom: str,
    age: int,
    email: Optional[str] = None,
    *roles: str,
    **metadata: Any
) -> dict[str, Any]:
    """
    Crée un utilisateur avec paramètres flexibles.

    Args:
        nom: Nom de famille (requis)
        prenom: Prénom (requis)
        age: Age (requis)
        email: Adresse email (optionnel)
        *roles: Rôles variables de l'utilisateur
        **metadata: Métadonnées additionnelles

    Returns:
        Dictionnaire représentant l'utilisateur
    """
    utilisateur = {
        "nom": nom,
        "prenom": prenom,
        "age": age,
        "email": email or f"{prenom.lower()}.{nom.lower()}@example.com",
        "roles": list(roles) if roles else ["user"],
        "metadata": metadata
    }
    return utilisateur

# Fonctions de première classe (first-class functions)
def appliquer_operation(
    nombres: List[float],
    operation: Callable[[float], float]
) -> List[float]:
    """
    Applique une opération à chaque nombre.

    Args:
        nombres: Liste de nombres
        operation: Fonction à appliquer

    Returns:
        Liste des résultats
    """
    return [operation(n) for n in nombres]

# Lambda functions
doubler = lambda x: x * 2
carre = lambda x: x ** 2
est_pair = lambda x: x % 2 == 0

# Closures (fermetures)
def creer_multiplicateur(facteur: int) -> Callable[[int], int]:
    """
    Crée une fonction de multiplication.

    Args:
        facteur: Le facteur de multiplication

    Returns:
        Fonction qui multiplie par le facteur
    """
    def multiplicateur(x: int) -> int:
        return x * facteur
    return multiplicateur

# Décorateurs
def chronometrer(func: Callable) -> Callable:
    """
    Décorateur pour mesurer le temps d'exécution.

    Args:
        func: Fonction à chronométrer

    Returns:
        Fonction décorée
    """
    @wraps(func)
    def wrapper(*args: Any, **kwargs: Any) -> Any:
        debut = time.time()
        resultat = func(*args, **kwargs)
        fin = time.time()
        print(f"{func.__name__} a pris {fin - debut:.4f} secondes")
        return resultat
    return wrapper

def valider_arguments(**validations: Callable) -> Callable:
    """
    Décorateur pour valider les arguments d'une fonction.

    Args:
        **validations: Dictionnaire {nom_arg: fonction_validation}

    Returns:
        Décorateur
    """
    def decorateur(func: Callable) -> Callable:
        @wraps(func)
        def wrapper(*args: Any, **kwargs: Any) -> Any:
            # Récupération des noms d'arguments
            import inspect
            sig = inspect.signature(func)
            bound_args = sig.bind(*args, **kwargs)
            bound_args.apply_defaults()

            # Validation
            for nom_arg, validateur in validations.items():
                if nom_arg in bound_args.arguments:
                    valeur = bound_args.arguments[nom_arg]
                    if not validateur(valeur):
                        raise ValueError(
                            f"Validation échouée pour {nom_arg}={valeur}"
                        )

            return func(*args, **kwargs)
        return wrapper
    return decorateur

# Générateurs
def generer_fibonacci(n: int):
    """
    Génère les n premiers nombres de Fibonacci.

    Args:
        n: Nombre d'éléments à générer

    Yields:
        Nombres de Fibonacci
    """
    a, b = 0, 1
    for * in range(n):
        yield a
        a, b = b, a + b

def lire_fichier_par_blocs(
    fichier: str,
    taille_bloc: int = 1024
):
    """
    Lit un fichier par blocs (économie de mémoire).

    Args:
        fichier: Chemin du fichier
        taille_bloc: Taille de chaque bloc

    Yields:
        Blocs de données
    """
    with open(fichier, 'r', encoding='utf-8') as f:
        while True:
            bloc = f.read(taille_bloc)
            if not bloc:
                break
            yield bloc

# Application pratique
@chronometrer
@valider_arguments(
    montant=lambda x: x > 0,
    reduction=lambda x: 0 <= x <= 1
)
def calculer_prix_final(
    montant: float,
    reduction: float = 0.0,
    code_promo: Optional[str] = None
) -> float:
    """
    Calcule le prix final avec réductions.

    Args:
        montant: Montant initial
        reduction: Taux de réduction (0 à 1)
        code_promo: Code promotionnel optionnel

    Returns:
        Prix final
    """
    prix_reduit = montant * (1 - reduction)

    # Application du code promo
    if code_promo == "BIENVENUE":
        prix_reduit *= 0.90
    elif code_promo == "VIP":
        prix_reduit *= 0.85

    return calculer_prix_ttc(prix_reduit)

if __name__ == "__main__":
    # Test création utilisateur
    user1 = creer_utilisateur(
        "Dupont",
        "Marie",
        28,
        roles=["user", "moderator"],
        ville="Paris",
        pays="France"
    )
    print("Utilisateur créé:", user1)

    print("n" + "="*50 + "n")

    # Test fonctions de première classe
    nombres = [1, 2, 3, 4, 5]
    doubles = appliquer_operation(nombres, doubler)
    carres = appliquer_operation(nombres, carre)
    print(f"Nombres: {nombres}")
    print(f"Doublés: {doubles}")
    print(f"Carrés: {carres}")

    print("n" + "="*50 + "n")

    # Test closures
    multiplier_par_3 = creer_multiplicateur(3)
    multiplier_par_10 = creer_multiplicateur(10)
    print(f"5 * 3 = {multiplier_par_3(5)}")
    print(f"5 * 10 = {multiplier_par_10(5)}")

    print("n" + "="*50 + "n")

    # Test fonction décorée
    prix_final = calculer_prix_final(100.0, 0.15, "BIENVENUE")
    print(f"Prix final: {prix_final:.2f}€")

    print("n" + "="*50 + "n")

    # Test générateur
    print("10 premiers nombres de Fibonacci:")
    for nombre in generer_fibonacci(10):
        print(nombre, end=" ")
    print()

Projet pratique : Gestionnaire de tâches

"""
task_manager.py - Gestionnaire de tâches complet
"""

from typing import List, Optional, Dict, Any
from datetime import datetime, timedelta
from enum import Enum
import json

class Priorite(Enum):
    """Niveaux de priorité des tâches."""
    BASSE = 1
    MOYENNE = 2
    HAUTE = 3
    URGENTE = 4

class StatutTache(Enum):
    """Statuts possibles d'une tâche."""
    A_FAIRE = "à faire"
    EN_COURS = "en cours"
    TERMINEE = "terminée"
    ANNULEE = "annulée"

class Tache:
    """Représente une tâche individuelle."""

    def __init__(
        self,
        titre: str,
        description: str = "",
        priorite: Priorite = Priorite.MOYENNE,
        echeance: Optional[datetime] = None
    ) -> None:
        """
        Initialise une tâche.

        Args:
            titre: Titre de la tâche
            description: Description détaillée
            priorite: Niveau de priorité
            echeance: Date limite
        """
        self.id: int = 0  # Sera assigné par le gestionnaire
        self.titre = titre
        self.description = description
        self.priorite = priorite
        self.statut = StatutTache.A_FAIRE
        self.date_creation = datetime.now()
        self.echeance = echeance
        self.tags: List[str] = []

    def est_en_retard(self) -> bool:
        """Vérifie si la tâche est en retard."""
        if not self.echeance or self.statut == StatutTache.TERMINEE:
            return False
        return datetime.now() > self.echeance

    def jours_restants(self) -> Optional[int]:
        """Calcule le nombre de jours restants."""
        if not self.echeance:
            return None
        delta = self.echeance - datetime.now()
        return delta.days

    def to_dict(self) -> Dict[str, Any]:
        """Convertit la tâche en dictionnaire."""
        return {
            "id": self.id,
            "titre": self.titre,
            "description": self.description,
            "priorite": self.priorite.name,
            "statut": self.statut.value,
            "date_creation": self.date_creation.isoformat(),
            "echeance": self.echeance.isoformat() if self.echeance else None,
            "tags": self.tags
        }

    def __str__(self) -> str:
        """Représentation textuelle de la tâche."""
        retard = " [EN RETARD]" if self.est_en_retard() else ""
        jours = self.jours_restants()
        echeance_str = f" (J-{jours})" if jours is not None else ""

        return (
            f"[{self.id}] {self.titre} - "
            f"{self.statut.value} - "
            f"Priorité: {self.priorite.name}"
            f"{echeance_str}{retard}"
        )

class GestionnaireTaches:
    """Gestionnaire principal des tâches."""

    def __init__(self) -> None:
        """Initialise le gestionnaire."""
        self.taches: Dict[int, Tache] = {}
        self._prochain_id: int = 1

    def ajouter_tache(
        self,
        titre: str,
        description: str = "",
        priorite: Priorite = Priorite.MOYENNE,
        echeance: Optional[datetime] = None,
        tags: Optional[List[str]] = None
    ) -> Tache:
        """
        Ajoute une nouvelle tâche.

        Args:
            titre: Titre de la tâche
            description: Description
            priorite: Niveau de priorité
            echeance: Date limite
            tags: Liste de tags

        Returns:
            La tâche créée
        """
        tache = Tache(titre, description, priorite, echeance)
        tache.id = self._prochain_id
        if tags:
            tache.tags = tags

        self.taches[self._prochain_id] = tache
        self._prochain_id += 1

        return tache

    def obtenir_tache(self, tache_id: int) -> Optional[Tache]:
        """Récupère une tâche par son ID."""
        return self.taches.get(tache_id)

    def supprimer_tache(self, tache_id: int) -> bool:
        """
        Supprime une tâche.

        Args:
            tache_id: ID de la tâche à supprimer

        Returns:
            True si supprimée, False sinon
        """
        if tache_id in self.taches:
            del self.taches[tache_id]
            return True
        return False

    def modifier_statut(
        self,
        tache_id: int,
        nouveau_statut: StatutTache
    ) -> bool:
        """Modifie le statut d'une tâche."""
        tache = self.obtenir_tache(tache_id)
        if tache:
            tache.statut = nouveau_statut
            return True
        return False

    def lister_taches(
        self,
        statut: Optional[StatutTache] = None,
        priorite: Optional[Priorite] = None,
        tag: Optional[str] = None
    ) -> List[Tache]:
        """
        Liste les tâches avec filtres optionnels.

        Args:
            statut: Filtrer par statut
            priorite: Filtrer par priorité
            tag: Filtrer par tag

        Returns:
            Liste des tâches correspondantes
        """
        resultats = list(self.taches.values())

        if statut:
            resultats = [t for t in resultats if t.statut == statut]

        if priorite:
            resultats = [t for t in resultats if t.priorite == priorite]

        if tag:
            resultats = [t for t in resultats if tag in t.tags]

        # Tri par priorité décroissante, puis par échéance
        resultats.sort(
            key=lambda t: (
                -t.priorite.value,
                t.echeance if t.echeance else datetime.max
            )
        )

        return resultats

    def taches_en_retard(self) -> List[Tache]:
        """Retourne les tâches en retard."""
        return [t for t in self.taches.values() if t.est_en_retard()]

    def taches_urgentes(self, jours: int = 3) -> List[Tache]:
        """
        Retourne les tâches urgentes (échéance proche).

        Args:
            jours: Nombre de jours pour considérer comme urgent

        Returns:
            Liste des tâches urgentes
        """
        resultats = []
        for tache in self.taches.values():
            jours_restants = tache.jours_restants()
            if (
                jours_restants is not None
                and 0 <= jours_restants <= jours
                and tache.statut != StatutTache.TERMINEE
            ):
                resultats.append(tache)
        return resultats

    def statistiques(self) -> Dict[str, Any]:
        """Calcule des statistiques sur les tâches."""
        total = len(self.taches)
        if total == 0:
            return {"total": 0}

        stats_statut = {}
        for statut in StatutTache:
            count = len([t for t in self.taches.values() if t.statut == statut])
            stats_statut[statut.value] = count

        stats_priorite = {}
        for priorite in Priorite:
            count = len([t for t in self.taches.values() if t.priorite == priorite])
            stats_priorite[priorite.name] = count

        return {
            "total": total,
            "par_statut": stats_statut,
            "par_priorite": stats_priorite,
            "en_retard": len(self.taches_en_retard()),
            "urgentes": len(self.taches_urgentes())
        }

    def sauvegarder(self, fichier: str) -> None:
        """
        Sauvegarde les tâches dans un fichier JSON.

        Args:
            fichier: Chemin du fichier de sauvegarde
        """
        donnees = {
            "prochain_id": self._prochain_id,
            "taches": [t.to_dict() for t in self.taches.values()]
        }

        with open(fichier, 'w', encoding='utf-8') as f:
            json.dump(donnees, f, indent=2, ensure_ascii=False)

    def charger(self, fichier: str) -> None:
        """
        Charge les tâches depuis un fichier JSON.

        Args:
            fichier: Chemin du fichier à charger
        """
        try:
            with open(fichier, 'r', encoding='utf-8') as f:
                donnees = json.load(f)

            self._prochain_id = donnees["prochain_id"]
            self.taches = {}

            for t_dict in donnees["taches"]:
                tache = Tache(
                    titre=t_dict["titre"],
                    description=t_dict["description"],
                    priorite=Priorite[t_dict["priorite"]]
                )
                tache.id = t_dict["id"]
                tache.statut = StatutTache(t_dict["statut"])
                tache.date_creation = datetime.fromisoformat(t_dict["date_creation"])
                if t_dict["echeance"]:
                    tache.echeance = datetime.fromisoformat(t_dict["echeance"])
                tache.tags = t_dict["tags"]

                self.taches[tache.id] = tache

        except FileNotFoundError:
            print(f"Fichier {fichier} non trouvé, démarrage avec une liste vide")

def demo_gestionnaire() -> None:
    """Démonstration du gestionnaire de tâches."""

    # Création du gestionnaire
    gestionnaire = GestionnaireTaches()

    # Ajout de tâches
    print("=== Ajout de tâches ===n")

    t1 = gestionnaire.ajouter_tache(
        "Apprendre Python",
        "Suivre un cours complet sur Python",
        Priorite.HAUTE,
        datetime.now() + timedelta(days=7),
        ["formation", "python"]
    )
    print(f"Tâche ajoutée: {t1}")

    t2 = gestionnaire.ajouter_tache(
        "Réunion d'équipe",
        "Présenter le nouveau projet",
        Priorite.URGENTE,
        datetime.now() + timedelta(days=1),
        ["travail", "reunion"]
    )
    print(f"Tâche ajoutée: {t2}")

    t3 = gestionnaire.ajouter_tache(
        "Faire les courses",
        "",
        Priorite.BASSE,
        datetime.now() + timedelta(days=2),
        ["personnel"]
    )
    print(f"Tâche ajoutée: {t3}")

    # Liste toutes les tâches
    print("n=== Toutes les tâches ===n")
    for tache in gestionnaire.lister_taches():
        print(tache)

    # Modification de statut
    print("n=== Modification de statuts ===n")
    gestionnaire.modifier_statut(t2.id, StatutTache.EN_COURS)
    print(f"Tâche {t2.id} passée en cours")

    gestionnaire.modifier_statut(t3.id, StatutTache.TERMINEE)
    print(f"Tâche {t3.id} terminée")

    # Filtrage
    print("n=== Tâches à faire ===n")
    for tache in gestionnaire.lister_taches(statut=StatutTache.A_FAIRE):
        print(tache)

    print("n=== Tâches urgentes (3 jours) ===n")
    for tache in gestionnaire.taches_urgentes(3):
        print(tache)

    # Statistiques
    print("n=== Statistiques ===n")
    stats = gestionnaire.statistiques()
    print(f"Total: {stats['total']}")
    print(f"Par statut: {stats['par_statut']}")
    print(f"Par priorité: {stats['par_priorite']}")
    print(f"En retard: {stats['en_retard']}")
    print(f"Urgentes: {stats['urgentes']}")

    # Sauvegarde
    print("n=== Sauvegarde ===n")
    gestionnaire.sauvegarder("/tmp/taches.json")
    print("Tâches sauvegardées dans /tmp/taches.json")

if __name__ == "__main__":
    demo_gestionnaire()

Tests avec pytest

"""
test_task_manager.py - Tests unitaires pour le gestionnaire de tâches
"""

import pytest
from datetime import datetime, timedelta
from task_manager import (
    Tache,
    GestionnaireTaches,
    Priorite,
    StatutTache
)

class TestTache:
    """Tests pour la classe Tache."""

    def test_creation_tache(self):
        """Test de création d'une tâche."""
        tache = Tache(
            "Test",
            "Description test",
            Priorite.HAUTE
        )

        assert tache.titre == "Test"
        assert tache.description == "Description test"
        assert tache.priorite == Priorite.HAUTE
        assert tache.statut == StatutTache.A_FAIRE

    def test_tache_en_retard(self):
        """Test de détection de retard."""
        # Tâche avec échéance passée
        tache_retard = Tache(
            "Retard",
            echeance=datetime.now() - timedelta(days=1)
        )
        assert tache_retard.est_en_retard() is True

        # Tâche avec échéance future
        tache_future = Tache(
            "Future",
            echeance=datetime.now() + timedelta(days=1)
        )
        assert tache_future.est_en_retard() is False

        # Tâche sans échéance
        tache_sans = Tache("Sans échéance")
        assert tache_sans.est_en_retard() is False

    def test_jours_restants(self):
        """Test du calcul des jours restants."""
        tache = Tache(
            "Test",
            echeance=datetime.now() + timedelta(days=5)
        )
        assert tache.jours_restants() == 5

        tache_sans = Tache("Sans échéance")
        assert tache_sans.jours_restants() is None

class TestGestionnaireTaches:
    """Tests pour le gestionnaire de tâches."""

    @pytest.fixture
    def gestionnaire(self):
        """Fixture pour créer un gestionnaire vide."""
        return GestionnaireTaches()

    @pytest.fixture
    def gestionnaire_avec_taches(self, gestionnaire):
        """Fixture pour créer un gestionnaire avec des tâches."""
        gestionnaire.ajouter_tache("Tâche 1", priorite=Priorite.HAUTE)
        gestionnaire.ajouter_tache("Tâche 2", priorite=Priorite.BASSE)
        gestionnaire.ajouter_tache("Tâche 3", priorite=Priorite.MOYENNE)
        return gestionnaire

    def test_ajouter_tache(self, gestionnaire):
        """Test d'ajout de tâche."""
        tache = gestionnaire.ajouter_tache(
            "Nouvelle tâche",
            "Description",
            Priorite.HAUTE
        )

        assert tache.id == 1
        assert tache.titre == "Nouvelle tâche"
        assert len(gestionnaire.taches) == 1

    def test_obtenir_tache(self, gestionnaire_avec_taches):
        """Test de récupération de tâche."""
        tache = gestionnaire_avec_taches.obtenir_tache(1)
        assert tache is not None
        assert tache.titre == "Tâche 1"

        tache_inexistante = gestionnaire_avec_taches.obtenir_tache(999)
        assert tache_inexistante is None

    def test_supprimer_tache(self, gestionnaire_avec_taches):
        """Test de suppression de tâche."""
        assert gestionnaire_avec_taches.supprimer_tache(1) is True
        assert len(gestionnaire_avec_taches.taches) == 2

        assert gestionnaire_avec_taches.supprimer_tache(999) is False

    def test_modifier_statut(self, gestionnaire_avec_taches):
        """Test de modification de statut."""
        assert gestionnaire_avec_taches.modifier_statut(1, StatutTache.TERMINEE)

        tache = gestionnaire_avec_taches.obtenir_tache(1)
        assert tache.statut == StatutTache.TERMINEE

    def test_lister_taches_filtres(self, gestionnaire_avec_taches):
        """Test de filtrage des tâches."""
        # Modifier quelques statuts
        gestionnaire_avec_taches.modifier_statut(1, StatutTache.TERMINEE)

        # Filtrer par statut
        a_faire = gestionnaire_avec_taches.lister_taches(
            statut=StatutTache.A_FAIRE
        )
        assert len(a_faire) == 2

        # Filtrer par priorité
        hautes = gestionnaire_avec_taches.lister_taches(
            priorite=Priorite.HAUTE
        )
        assert len(hautes) == 1

    def test_taches_urgentes(self, gestionnaire):
        """Test des tâches urgentes."""
        # Tâche urgente (2 jours)
        gestionnaire.ajouter_tache(
            "Urgente",
            echeance=datetime.now() + timedelta(days=2)
        )

        # Tâche non urgente (5 jours)
        gestionnaire.ajouter_tache(
            "Non urgente",
            echeance=datetime.now() + timedelta(days=5)
        )

        urgentes = gestionnaire.taches_urgentes(3)
        assert len(urgentes) == 1

    def test_statistiques(self, gestionnaire_avec_taches):
        """Test des statistiques."""
        gestionnaire_avec_taches.modifier_statut(1, StatutTache.TERMINEE)

        stats = gestionnaire_avec_taches.statistiques()

        assert stats["total"] == 3
        assert stats["par_statut"]["terminée"] == 1
        assert stats["par_statut"]["à faire"] == 2

if __name__ == "__main__":
    pytest.main([__file__, "-v"])

Fichier pytest.ini

# pytest.ini - Configuration pytest
[pytest]
testpaths = tests
python_files = test**.py
python_classes = Test*
python_functions = test**
addopts =
    -v
    --strict-markers
    --cov=.
    --cov-report=html
    --cov-report=term-missing

Bonnes pratiques et PEP

Configuration Black et MyPy

# pyproject.toml - Configuration des outils de qualité de code
[tool.black]
line-length = 88
target-version = ['py310']
include = '.pyi?$'
extend-exclude = '''
/(
  # directories
  .eggs
  | .git
  | .hg
  | .mypy_cache
  | .tox
  | .venv
  | build
  | dist
)/
'''

[tool.mypy]
python_version = "3.10"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
check_untyped_defs = true
no_implicit_optional = true
warn_redundant_casts = true
warn_unused_ignores = true
warn_no_return = true
strict_equality = true

[tool.pytest.ini_options]
minversion = "7.0"
addopts = "-ra -q --strict-markers"
testpaths = ["tests"]

Guide des bonnes pratiques

"""
best_practices.py - Démonstration des bonnes pratiques Python
"""

# PEP 8: Style de code
# - 4 espaces pour l'indentation (pas de tabulations)
# - Lignes max 79 caractères (88 avec Black)
# - 2 lignes blanches entre fonctions de niveau module
# - 1 ligne blanche entre méthodes de classe

# PEP 257: Docstrings
# - Toujours utiliser des docstrings pour modules, classes, fonctions
# - Format: triple guillemets """
# - Première ligne: résumé court
# - Lignes suivantes: détails

from typing import List, Optional, Protocol
from dataclasses import dataclass
from enum import Enum

# PEP 484: Type Hints
# - Toujours typer les paramètres et retours de fonction
# - Utiliser Optional[T] pour les valeurs pouvant être None
# - Utiliser Union[T1, T2] pour plusieurs types possibles

@dataclass
class Config:
    """
    Configuration de l'application.

    Utilise @dataclass pour éviter le code boilerplate.
    """
    nom: str
    version: str
    debug: bool = False
    max_connexions: int = 100

class Loggable(Protocol):
    """
    Protocol pour les objets loggables (PEP 544).

    Les protocols définissent des interfaces sans héritage.
    """
    def log(self, message: str) -> None:
        """Log un message."""
        ...

# Bonnes pratiques de nommage
# - snake_case pour fonctions et variables
# - PascalCase pour classes
# - MAJUSCULES pour constantes
# - *prefixe pour méthodes privées

CONSTANTE_GLOBALE: int = 42

def fonction_publique(parametre: str) -> str:
    """Fonction accessible publiquement."""
    return *fonction_privee(parametre)

def *fonction_privee(parametre: str) -> str:
    """Fonction d'usage interne (convention)."""
    return parametre.upper()

# Context managers (with statement)
class GestionnaireRessource:
    """
    Gestionnaire de ressource utilisant le protocole context manager.

    Assure le nettoyage même en cas d'erreur.
    """

    def __init__(self, nom: str) -> None:
        """Initialise le gestionnaire."""
        self.nom = nom
        self.ressource: Optional[str] = None

    def __enter__(self) -> "GestionnaireRessource":
        """Appelé au début du bloc with."""
        print(f"Ouverture de {self.nom}")
        self.ressource = f"Ressource {self.nom}"
        return self

    def __exit__(self, exc_type, exc_val, exc_tb) -> None:
        """Appelé à la fin du bloc with."""
        print(f"Fermeture de {self.nom}")
        self.ressource = None

# EAFP vs LBYL
# Python préfère EAFP (Easier to Ask for Forgiveness than Permission)
# plutôt que LBYL (Look Before You Leap)

def eafp_exemple(dictionnaire: dict, cle: str) -> Optional[str]:
    """Exemple EAFP (préféré en Python)."""
    try:
        return dictionnaire[cle]
    except KeyError:
        return None

def lbyl_exemple(dictionnaire: dict, cle: str) -> Optional[str]:
    """Exemple LBYL (moins pythonique)."""
    if cle in dictionnaire:
        return dictionnaire[cle]
    return None

# List comprehensions vs boucles for
# Préférer les comprehensions quand c'est lisible

def filtrer_nombres_pythonique(nombres: List[int]) -> List[int]:
    """Version pythonique avec comprehension."""
    return [n for n in nombres if n > 0 and n % 2 == 0]

def filtrer_nombres_verbeux(nombres: List[int]) -> List[int]:
    """Version verbeuse (éviter si possible)."""
    resultat = []
    for n in nombres:
        if n > 0:
            if n % 2 == 0:
                resultat.append(n)
    return resultat

if __name__ == "__main__":
    # Test dataclass
    config = Config("MonApp", "1.0.0", debug=True)
    print(f"Config: {config}")

    # Test context manager
    with GestionnaireRessource("test.txt") as gestion:
        print(f"Utilisation de: {gestion.ressource}")

    # Comparaison EAFP vs LBYL
    dico = {"cle1": "valeur1"}
    print(eafp_exemple(dico, "cle1"))
    print(eafp_exemple(dico, "cle_inexistante"))

    # Comprehensions
    nombres = [-2, -1, 0, 1, 2, 3, 4, 5, 6]
    print(filtrer_nombres_pythonique(nombres))

Conclusion

Vous avez maintenant une base solide en Python avec :

  • Syntaxe et types de base : Variables, opérateurs, types primitifs
  • Structures de données : Listes, dictionnaires, tuples, sets
  • Structures de contrôle : Conditions, boucles, comprehensions
  • Fonctions avancées : Arguments flexibles, closures, décorateurs, générateurs
  • Projet pratique : Gestionnaire de tâches complet
  • Tests : pytest pour assurer la qualité
  • Bonnes pratiques* : PEP 8, PEP 257, type hints
  • Prochaines étapes

  • Approfondir la programmation orientée objet (article suivant)
  • Apprendre les frameworks web (FastAPI, Django)
  • Maîtriser les bases de données avec SQLAlchemy
  • Explorer la programmation asynchrone
  • Découvrir le Data Science avec Pandas et NumPy
  • Ressources complémentaires

  • Documentation officielle Python: https://docs.python.org/fr/3/
  • PEP 8 Style Guide: https://pep8.org/
  • Real Python tutorials: https://realpython.com/
  • Python Package Index (PyPI): https://pypi.org/

Continuez à pratiquer avec des projets réels et n’hésitez pas à explorer les bibliothèques de l’écosystème Python.

Une remarque, un retour ?

Cet article est vivant - corrections, contre-arguments et retours de production sont les bienvenus. Trois canaux, choisissez celui qui vous convient.

Laisser un commentaire