diff --git a/Base_Python.md b/Base_Python.md new file mode 100644 index 0000000..ff6e35e --- /dev/null +++ b/Base_Python.md @@ -0,0 +1,452 @@ +# Les Bases de Python + +## Table des matières +1. [Introduction à Python](#introduction-à-python) +2. [Variables et Types de données](#variables-et-types-de-données) +3. [Opérateurs](#opérateurs) +4. [Structures de données de base](#structures-de-données-de-base) +5. [Syntaxe de base et indentation](#syntaxe-de-base-et-indentation) +6. [Entrées et sorties](#entrées-et-sorties) +7. [Commentaires](#commentaires) +8. [Conventions de nommage](#conventions-de-nommage) + +## Introduction à Python + +Python est un langage de programmation de haut niveau, interprété, et orienté objet. Il est largement utilisé en data science pour sa simplicité et sa puissance. + +### Caractéristiques principales : +- **Syntaxe claire et lisible** +- **Multiparadigme** (procédural, orienté objet, fonctionnel) +- **Typage dynamique** +- **Grande bibliothèque standard** +- **Communauté active** + +## Variables et Types de données + +### Déclaration de variables +En Python, pas besoin de déclarer explicitement le type d'une variable : + +```python +# Déclaration simple +nom = "Alice" +age = 25 +taille = 1.65 +est_etudiant = True +``` + +### Types de données primitifs + +#### 1. Entiers (int) +```python +nombre_entier = 42 +nombre_negatif = -10 +grande_valeur = 1000000 + +# Opérations de base +addition = 5 + 3 # 8 +soustraction = 10 - 4 # 6 +multiplication = 6 * 7 # 42 +division = 15 / 3 # 5.0 (résultat en float) +division_entiere = 15 // 4 # 3 +modulo = 15 % 4 # 3 +puissance = 2 ** 3 # 8 +``` + +#### 2. Nombres à virgule flottante (float) +```python +pi = 3.14159 +temperature = -2.5 +notation_scientifique = 1.5e6 # 1500000.0 + +# Méthodes utiles +import math +print(math.ceil(3.2)) # 4 (arrondi supérieur) +print(math.floor(3.8)) # 3 (arrondi inférieur) +print(round(3.7)) # 4 (arrondi standard) +``` + +#### 3. Chaînes de caractères (str) +```python +# Différentes façons de créer des chaînes +prenom = "Jean" +nom = 'Dupont' +citation = """Cette phrase +s'étend sur plusieurs lignes""" + +# Opérations sur les chaînes +concatenation = prenom + " " + nom # "Jean Dupont" +repetition = "Ha" * 3 # "HaHaHa" +longueur = len(prenom) # 4 + +# Méthodes importantes +message = "Bonjour le monde" +print(message.upper()) # "BONJOUR LE MONDE" +print(message.lower()) # "bonjour le monde" +print(message.title()) # "Bonjour Le Monde" +print(message.replace("monde", "Python")) # "Bonjour le Python" +print(message.split()) # ['Bonjour', 'le', 'monde'] + +# Formatage de chaînes +nom = "Alice" +age = 30 +# Méthode format() +message1 = "Je m'appelle {} et j'ai {} ans".format(nom, age) +# f-strings (recommandé, Python 3.6+) +message2 = f"Je m'appelle {nom} et j'ai {age} ans" +``` + +#### 4. Booléens (bool) +```python +est_vrai = True +est_faux = False + +# Opérateurs logiques +et_logique = True and False # False +ou_logique = True or False # True +non_logique = not True # False + +# Valeurs considérées comme False +print(bool(0)) # False +print(bool("")) # False +print(bool([])) # False +print(bool(None)) # False + +# Valeurs considérées comme True +print(bool(1)) # True +print(bool("texte")) # True +print(bool([1, 2])) # True +``` + +## Opérateurs + +### Opérateurs arithmétiques +```python +a, b = 10, 3 + +addition = a + b # 13 +soustraction = a - b # 7 +multiplication = a * b # 30 +division = a / b # 3.333... +division_entiere = a // b # 3 +modulo = a % b # 1 +puissance = a ** b # 1000 +``` + +### Opérateurs de comparaison +```python +x, y = 5, 3 + +egal = x == y # False +different = x != y # True +superieur = x > y # True +superieur_egal = x >= y # True +inferieur = x < y # False +inferieur_egal = x <= y # False +``` + +### Opérateurs d'assignation +```python +x = 10 +x += 5 # x = x + 5, donc x = 15 +x -= 3 # x = x - 3, donc x = 12 +x *= 2 # x = x * 2, donc x = 24 +x /= 4 # x = x / 4, donc x = 6.0 +x //= 2 # x = x // 2, donc x = 3.0 +x %= 2 # x = x % 2, donc x = 1.0 +x **= 3 # x = x ** 3, donc x = 1.0 +``` + +## Structures de données de base + +### Listes (list) +```python +# Création de listes +fruits = ["pomme", "banane", "orange"] +nombres = [1, 2, 3, 4, 5] +mixte = [1, "texte", True, 3.14] + +# Accès aux éléments (indexation commence à 0) +premier_fruit = fruits[0] # "pomme" +dernier_fruit = fruits[-1] # "orange" + +# Modification +fruits[1] = "mangue" # ["pomme", "mangue", "orange"] + +# Méthodes importantes +fruits.append("kiwi") # Ajoute à la fin +fruits.insert(1, "poire") # Insert à l'index 1 +supprime = fruits.pop() # Supprime et retourne le dernier +fruits.remove("pomme") # Supprime la première occurrence + +# Slicing (découpage) +sous_liste = nombres[1:4] # [2, 3, 4] +debut = nombres[:3] # [1, 2, 3] +fin = nombres[2:] # [3, 4, 5] +``` + +### Tuples (tuple) +```python +# Création (immutables) +coordonnees = (10, 20) +couleurs = ("rouge", "vert", "bleu") + +# Accès (comme les listes) +x = coordonnees[0] # 10 +y = coordonnees[1] # 20 + +# Déballage (unpacking) +x, y = coordonnees # x = 10, y = 20 +``` + +### Dictionnaires (dict) +```python +# Création +personne = { + "nom": "Dupont", + "prenom": "Jean", + "age": 30, + "ville": "Paris" +} + +# Accès +nom = personne["nom"] # "Dupont" +age = personne.get("age", 0) # 30 (0 si clé n'existe pas) + +# Modification +personne["age"] = 31 +personne["profession"] = "Ingénieur" + +# Méthodes utiles +cles = personne.keys() # dict_keys(['nom', 'prenom', 'age', 'ville']) +valeurs = personne.values() # dict_values(['Dupont', 'Jean', 31, 'Paris']) +items = personne.items() # Paires clé-valeur +``` + +### Ensembles (set) +```python +# Création +nombres = {1, 2, 3, 4, 5} +voyelles = set("aeiou") + +# Opérations sur les ensembles +ensemble1 = {1, 2, 3, 4} +ensemble2 = {3, 4, 5, 6} + +union = ensemble1 | ensemble2 # {1, 2, 3, 4, 5, 6} +intersection = ensemble1 & ensemble2 # {3, 4} +difference = ensemble1 - ensemble2 # {1, 2} +``` + +## Syntaxe de base et indentation + +### Indentation +Python utilise l'indentation pour définir les blocs de code : + +```python +# Correct +if True: + print("Ceci est indenté") + print("Ceci aussi") +print("Ceci n'est pas indenté") + +# Incorrect - IndentationError +# if True: +# print("Erreur d'indentation") +``` + +### Structure conditionnelle basique +```python +age = 18 + +if age >= 18: + print("Majeur") +elif age >= 13: + print("Adolescent") +else: + print("Enfant") +``` + +### Instruction pass +```python +# Placeholder pour du code à venir +def fonction_vide(): + pass # Ne fait rien, évite l'erreur de syntaxe + +if condition: + pass # À implémenter plus tard +``` + +## Entrées et sorties + +### Fonction print() +```python +# Affichage simple +print("Hello, World!") + +# Plusieurs arguments +print("Nom:", "Alice", "Age:", 25) + +# Séparateur personnalisé +print("un", "deux", "trois", sep="-") # un-deux-trois + +# Fin personnalisée +print("Ligne 1", end=" ") +print("Suite de la ligne") # Ligne 1 Suite de la ligne + +# Formatage +nom = "Bob" +print(f"Bonjour {nom}!") +``` + +### Fonction input() +```python +# Saisie utilisateur (retourne toujours une chaîne) +nom = input("Entrez votre nom: ") +print(f"Bonjour {nom}!") + +# Conversion de type nécessaire +age_str = input("Entrez votre âge: ") +age = int(age_str) # Conversion en entier + +# Version compacte +age = int(input("Entrez votre âge: ")) +``` + +## Commentaires + +### Commentaires sur une ligne +```python +# Ceci est un commentaire +x = 5 # Commentaire en fin de ligne +``` + +### Commentaires multilignes +```python +""" +Ceci est un commentaire +sur plusieurs lignes +(docstring) +""" + +''' +Alternative avec +guillemets simples +''' +``` + +### Docstrings pour la documentation +```python +def ma_fonction(x, y): + """ + Cette fonction additionne deux nombres. + + Args: + x (int): Premier nombre + y (int): Deuxième nombre + + Returns: + int: La somme de x et y + """ + return x + y +``` + +## Conventions de nommage + +### Variables et fonctions +```python +# Snake_case (recommandé) +nom_utilisateur = "Alice" +nombre_total = 100 + +def calculer_moyenne(notes): + pass +``` + +### Constantes +```python +# Majuscules avec underscores +PI = 3.14159 +VITESSE_LUMIERE = 299792458 +``` + +### Classes +```python +# PascalCase +class PersonneEtudiante: + pass +``` + +### Conventions générales +- **Noms descriptifs** : `age_utilisateur` plutôt que `a` +- **Éviter les mots-clés** : pas de `class`, `def`, `if`, etc. +- **Commencer par une lettre** : pas de chiffres au début +- **Pas d'espaces** : utiliser `_` pour séparer les mots + +## Exercices pratiques + +### Exercice 1 : Variables et types +```python +# Créez des variables pour stocker : +# - Votre nom (chaîne) +# - Votre âge (entier) +# - Votre taille en mètres (float) +# - Si vous êtes étudiant (booléen) +# Affichez ces informations de manière formatée + +nom = "Alice" +age = 22 +taille = 1.68 +est_etudiant = True + +print(f"Je m'appelle {nom}, j'ai {age} ans, je mesure {taille}m") +if est_etudiant: + print("Je suis étudiant(e)") +``` + +### Exercice 2 : Manipulation de chaînes +```python +# Créez une phrase, puis : +# - Affichez-la en majuscules +# - Comptez le nombre de mots +# - Remplacez un mot par un autre + +phrase = "Python est un langage fantastique" +print(phrase.upper()) +mots = phrase.split() +print(f"Nombre de mots : {len(mots)}") +nouvelle_phrase = phrase.replace("fantastique", "puissant") +print(nouvelle_phrase) +``` + +### Exercice 3 : Listes et dictionnaires +```python +# Créez une liste de vos 5 films préférés +# Créez un dictionnaire avec des informations sur vous +# Combinez les deux structures + +films_preferes = ["Matrix", "Inception", "Interstellar", "Blade Runner", "Her"] + +profil = { + "nom": "Jean", + "age": 25, + "ville": "Lyon", + "films_preferes": films_preferes +} + +print(f"{profil['nom']} habite à {profil['ville']}") +print(f"Son film préféré est : {profil['films_preferes'][0]}") +``` + +## Résumé + +Ce chapitre couvre les fondamentaux de Python : + +1. **Variables** : Stockage de données avec typage dynamique +2. **Types primitifs** : int, float, str, bool +3. **Structures de données** : list, tuple, dict, set +4. **Opérateurs** : Arithmétiques, comparaison, assignation +5. **Syntaxe** : Indentation obligatoire, commentaires +6. **I/O** : print() pour l'affichage, input() pour la saisie +7. **Conventions** : Nommage cohérent et lisible + +Ces concepts forment la base de tout programme Python. Maîtriser ces éléments est essentiel avant d'aborder des sujets plus avancés comme les boucles, les fonctions et les modules. \ No newline at end of file diff --git a/Fonction.md b/Fonction.md new file mode 100644 index 0000000..c7b3040 --- /dev/null +++ b/Fonction.md @@ -0,0 +1,1246 @@ +# Les Fonctions en Python - Guide Complet + +## Table des matières +1. [Introduction aux fonctions](#introduction-aux-fonctions) +2. [Syntaxe de base](#syntaxe-de-base) +3. [Paramètres et arguments](#paramètres-et-arguments) +4. [Valeurs de retour](#valeurs-de-retour) +5. [Portée des variables (Scope)](#portée-des-variables-scope) +6. [Fonctions lambda](#fonctions-lambda) +7. [Fonctions d'ordre supérieur](#fonctions-dordre-supérieur) +8. [Décorateurs](#décorateurs) +9. [Générateurs et yield](#générateurs-et-yield) +10. [Fonctions récursives](#fonctions-récursives) +11. [Gestion d'erreurs dans les fonctions](#gestion-derreurs-dans-les-fonctions) +12. [Techniques avancées](#techniques-avancées) +13. [Bonnes pratiques](#bonnes-pratiques) + +## Introduction aux fonctions + +Les fonctions sont des blocs de code réutilisables qui effectuent une tâche spécifique. Elles permettent de : +- **Éviter la répétition** de code +- **Organiser** le programme en modules logiques +- **Faciliter les tests** et le débogage +- **Améliorer la lisibilité** du code + +### Pourquoi utiliser des fonctions ? +```python +# Sans fonction - répétition de code +nom1 = "Alice" +print(f"Bonjour {nom1}!") +print(f"Comment allez-vous, {nom1}?") + +nom2 = "Bob" +print(f"Bonjour {nom2}!") +print(f"Comment allez-vous, {nom2}?") + +# Avec fonction - code réutilisable +def saluer(nom): + print(f"Bonjour {nom}!") + print(f"Comment allez-vous, {nom}?") + +saluer("Alice") +saluer("Bob") +``` + +## Syntaxe de base + +### Définition d'une fonction +```python +def nom_fonction(parametres): + """Documentation de la fonction (docstring)""" + # Corps de la fonction + # Instructions + return valeur # Optionnel +``` + +### Exemples simples +```python +# Fonction sans paramètre ni retour +def dire_bonjour(): + """Affiche un message de salutation""" + print("Bonjour tout le monde!") + +dire_bonjour() + +# Fonction avec paramètre mais sans retour +def saluer(nom): + """Salue une personne par son nom""" + print(f"Bonjour {nom}!") + +saluer("Marie") + +# Fonction avec paramètre et retour +def doubler(nombre): + """Retourne le double d'un nombre""" + return nombre * 2 + +resultat = doubler(5) # resultat = 10 + +# Fonction avec plusieurs paramètres +def additionner(a, b): + """Additionne deux nombres""" + return a + b + +somme = additionner(3, 7) # somme = 10 +``` + +### Documentation (Docstrings) +```python +def calculer_aire_rectangle(longueur, largeur): + """ + Calcule l'aire d'un rectangle. + + Args: + longueur (float): La longueur du rectangle + largeur (float): La largeur du rectangle + + Returns: + float: L'aire du rectangle + + Raises: + ValueError: Si longueur ou largeur est négative + """ + if longueur < 0 or largeur < 0: + raise ValueError("Les dimensions doivent être positives") + + return longueur * largeur + +# Accéder à la documentation +print(calculer_aire_rectangle.__doc__) +help(calculer_aire_rectangle) +``` + +## Paramètres et arguments + +### Arguments positionnels +```python +def presenter_personne(nom, age, ville): + return f"{nom}, {age} ans, habite à {ville}" + +# Ordre important +info = presenter_personne("Alice", 25, "Paris") +``` + +### Arguments nommés (mots-clés) +```python +def presenter_personne(nom, age, ville): + return f"{nom}, {age} ans, habite à {ville}" + +# Ordre peu important avec les noms +info = presenter_personne(age=25, ville="Paris", nom="Alice") +info = presenter_personne("Alice", ville="Paris", age=25) # Mixte +``` + +### Valeurs par défaut +```python +def saluer(nom, message="Bonjour", ponctuation="!"): + return f"{message} {nom}{ponctuation}" + +print(saluer("Alice")) # "Bonjour Alice!" +print(saluer("Bob", "Salut")) # "Salut Bob!" +print(saluer("Charlie", "Hey", ".")) # "Hey Charlie." + +# Attention aux valeurs mutables par défaut ! +def ajouter_element(element, liste=None): # Correct + if liste is None: + liste = [] + liste.append(element) + return liste + +# Incorrect - à éviter ! +def ajouter_element_incorrect(element, liste=[]): + liste.append(element) # La même liste est réutilisée ! + return liste +``` + +### *args - Arguments variables positionnels +```python +def additionner(*nombres): + """Additionne un nombre variable d'arguments""" + return sum(nombres) + +print(additionner(1, 2, 3)) # 6 +print(additionner(1, 2, 3, 4, 5)) # 15 + +def afficher_info(nom, *autres_infos): + print(f"Nom: {nom}") + for i, info in enumerate(autres_infos, 1): + print(f"Info {i}: {info}") + +afficher_info("Alice", "25 ans", "Paris", "Ingénieure") +``` + +### **kwargs - Arguments variables nommés +```python +def creer_profil(**kwargs): + """Crée un profil avec des informations variables""" + profil = {} + for cle, valeur in kwargs.items(): + profil[cle] = valeur + return profil + +profil1 = creer_profil(nom="Alice", age=25, ville="Paris") +profil2 = creer_profil(nom="Bob", profession="Médecin", salaire=50000) + +def fonction_complete(arg_obligatoire, arg_defaut="défaut", *args, **kwargs): + print(f"Obligatoire: {arg_obligatoire}") + print(f"Défaut: {arg_defaut}") + print(f"Args supplémentaires: {args}") + print(f"Kwargs: {kwargs}") + +fonction_complete("test", "nouveau", 1, 2, 3, nom="Alice", age=25) +``` + +### Déballage d'arguments +```python +def additionner(a, b, c): + return a + b + c + +# Déballage de liste/tuple avec * +nombres = [1, 2, 3] +resultat = additionner(*nombres) # Équivalent à additionner(1, 2, 3) + +# Déballage de dictionnaire avec ** +def presenter_personne(nom, age, ville): + return f"{nom}, {age} ans, habite à {ville}" + +infos = {"nom": "Alice", "age": 25, "ville": "Paris"} +presentation = presenter_personne(**infos) +``` + +## Valeurs de retour + +### Retour simple +```python +def carre(x): + return x ** 2 + +def est_pair(nombre): + return nombre % 2 == 0 + +def obtenir_signe(nombre): + if nombre > 0: + return "positif" + elif nombre < 0: + return "négatif" + else: + return "zéro" +``` + +### Retours multiples +```python +def divmod_personnalise(dividende, diviseur): + """Retourne quotient et reste""" + quotient = dividende // diviseur + reste = dividende % diviseur + return quotient, reste # Retourne un tuple + +# Déballage du retour +q, r = divmod_personnalise(17, 5) # q = 3, r = 2 + +def analyser_texte(texte): + """Analyse un texte et retourne plusieurs métriques""" + nb_mots = len(texte.split()) + nb_caracteres = len(texte) + nb_voyelles = sum(1 for char in texte.lower() if char in 'aeiou') + + return { + 'mots': nb_mots, + 'caracteres': nb_caracteres, + 'voyelles': nb_voyelles + } + +stats = analyser_texte("Bonjour le monde") +print(stats['mots']) # 3 +``` + +### Retour conditionnel +```python +def trouver_element(liste, element): + """Trouve un élément dans une liste""" + for i, item in enumerate(liste): + if item == element: + return i # Sortie immédiate de la fonction + return None # Si non trouvé + +index = trouver_element([1, 2, 3, 4], 3) # 2 +index = trouver_element([1, 2, 3, 4], 5) # None +``` + +## Portée des variables (Scope) + +### Variables locales et globales +```python +# Variable globale +compteur_global = 0 + +def incrementer(): + # Variable locale + compteur_local = 1 + print(f"Local: {compteur_local}") + print(f"Global (lecture): {compteur_global}") + +def modifier_global(): + global compteur_global # Nécessaire pour modifier + compteur_global += 1 + +def exemple_local(): + x = 10 # Variable locale + def interne(): + print(x) # Accès à la variable de la fonction englobante + interne() + +exemple_local() +``` + +### Règle LEGB (Local, Enclosing, Global, Built-in) +```python +# Built-in +print(len([1, 2, 3])) # len est une fonction built-in + +# Global +variable_globale = "global" + +def externe(): + # Enclosing + variable_enclosing = "enclosing" + + def interne(): + # Local + variable_locale = "local" + print(f"Local: {variable_locale}") + print(f"Enclosing: {variable_enclosing}") + print(f"Global: {variable_globale}") + print(f"Built-in: {len([1, 2, 3])}") + + interne() + +externe() +``` + +### nonlocal +```python +def compteur(): + compte = 0 + + def incrementer(): + nonlocal compte # Référence à la variable de la fonction englobante + compte += 1 + return compte + + return incrementer + +mon_compteur = compteur() +print(mon_compteur()) # 1 +print(mon_compteur()) # 2 +print(mon_compteur()) # 3 +``` + +## Fonctions lambda + +### Syntaxe de base +```python +# Fonction lambda simple +carre = lambda x: x ** 2 +print(carre(5)) # 25 + +# Équivalent avec def +def carre_normale(x): + return x ** 2 + +# Lambda avec plusieurs paramètres +additionner = lambda x, y: x + y +print(additionner(3, 4)) # 7 + +# Lambda avec conditions +maximum = lambda a, b: a if a > b else b +print(maximum(10, 7)) # 10 +``` + +### Utilisation avec les fonctions built-in +```python +nombres = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + +# map() avec lambda +carres = list(map(lambda x: x**2, nombres)) +# [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] + +# filter() avec lambda +pairs = list(filter(lambda x: x % 2 == 0, nombres)) +# [2, 4, 6, 8, 10] + +# sorted() avec lambda +personnes = [ + {"nom": "Alice", "age": 30}, + {"nom": "Bob", "age": 25}, + {"nom": "Charlie", "age": 35} +] + +# Tri par âge +tri_age = sorted(personnes, key=lambda p: p["age"]) +# [{"nom": "Bob", "age": 25}, {"nom": "Alice", "age": 30}, {"nom": "Charlie", "age": 35}] +``` + +### Limites des lambdas +```python +# Lambda - seulement des expressions simples +# Pas de : print(), if complet, for, while, etc. + +# Bon usage de lambda +nombres.sort(key=lambda x: -x) # Tri décroissant + +# Mauvais usage - mieux vaut une fonction normale +# lambda x: print(x) if x > 0 else None # Difficile à lire + +# Mieux : +def afficher_si_positif(x): + if x > 0: + print(x) +``` + +## Fonctions d'ordre supérieur + +### Fonctions qui prennent d'autres fonctions en paramètres +```python +def appliquer_operation(liste, operation): + """Applique une opération à chaque élément d'une liste""" + return [operation(x) for x in liste] + +def double(x): + return x * 2 + +def carre(x): + return x ** 2 + +nombres = [1, 2, 3, 4, 5] +doubles = appliquer_operation(nombres, double) # [2, 4, 6, 8, 10] +carres = appliquer_operation(nombres, carre) # [1, 4, 9, 16, 25] + +# Avec lambda +cubes = appliquer_operation(nombres, lambda x: x**3) # [1, 8, 27, 64, 125] +``` + +### Fonctions qui retournent d'autres fonctions +```python +def creer_multiplicateur(facteur): + """Crée une fonction qui multiplie par un facteur donné""" + def multiplicateur(x): + return x * facteur + return multiplicateur + +doubler = creer_multiplicateur(2) +tripler = creer_multiplicateur(3) + +print(doubler(5)) # 10 +print(tripler(5)) # 15 + +def creer_validateur(minimum, maximum): + """Crée une fonction de validation""" + def valider(valeur): + return minimum <= valeur <= maximum + return valider + +valider_age = creer_validateur(0, 150) +print(valider_age(25)) # True +print(valider_age(-5)) # False +``` + +### Closures (Fermetures) +```python +def compteur_avec_pas(pas=1): + """Crée un compteur avec un pas personnalisé""" + compte = 0 + + def incrementer(): + nonlocal compte + compte += pas + return compte + + def decrementar(): + nonlocal compte + compte -= pas + return compte + + def obtenir_compte(): + return compte + + # Retourne un dictionnaire de fonctions + return { + 'incrementer': incrementer, + 'decrementer': decrementar, + 'valeur': obtenir_compte + } + +compteur = compteur_avec_pas(5) +print(compteur['incrementer']()) # 5 +print(compteur['incrementer']()) # 10 +print(compteur['decrementer']()) # 5 +``` + +## Décorateurs + +### Concept de base +```python +def mon_decorateur(func): + """Décorateur simple qui ajoute un comportement""" + def wrapper(*args, **kwargs): + print(f"Avant l'exécution de {func.__name__}") + resultat = func(*args, **kwargs) + print(f"Après l'exécution de {func.__name__}") + return resultat + return wrapper + +# Utilisation avec @ +@mon_decorateur +def dire_bonjour(nom): + print(f"Bonjour {nom}!") + +dire_bonjour("Alice") +# Sortie: +# Avant l'exécution de dire_bonjour +# Bonjour Alice! +# Après l'exécution de dire_bonjour +``` + +### Décorateurs utiles +```python +import time +from functools import wraps + +def mesurer_temps(func): + """Décorateur qui mesure le temps d'exécution""" + @wraps(func) # Préserve les métadonnées de la fonction originale + def wrapper(*args, **kwargs): + debut = time.time() + resultat = func(*args, **kwargs) + fin = time.time() + print(f"{func.__name__} executée en {fin - debut:.4f} secondes") + return resultat + return wrapper + +def cache_simple(func): + """Décorateur de cache simple""" + cache = {} + + @wraps(func) + def wrapper(*args): + if args in cache: + print(f"Cache hit pour {args}") + return cache[args] + + resultat = func(*args) + cache[args] = resultat + print(f"Résultat mis en cache pour {args}") + return resultat + + return wrapper + +@mesurer_temps +@cache_simple +def fibonacci(n): + """Calcule le nième nombre de Fibonacci""" + if n <= 1: + return n + return fibonacci(n-1) + fibonacci(n-2) + +print(fibonacci(10)) +``` + +### Décorateurs avec paramètres +```python +def repeter(nb_fois): + """Décorateur qui répète l'exécution d'une fonction""" + def decorateur(func): + @wraps(func) + def wrapper(*args, **kwargs): + for i in range(nb_fois): + resultat = func(*args, **kwargs) + return resultat + return wrapper + return decorateur + +@repeter(3) +def saluer(nom): + print(f"Salut {nom}!") + +saluer("Bob") +# Sortie: +# Salut Bob! +# Salut Bob! +# Salut Bob! +``` + +### Décorateurs de classe +```python +def singleton(cls): + """Décorateur qui transforme une classe en singleton""" + instances = {} + + def get_instance(*args, **kwargs): + if cls not in instances: + instances[cls] = cls(*args, **kwargs) + return instances[cls] + + return get_instance + +@singleton +class DatabaseConnection: + def __init__(self): + print("Création de la connexion à la base de données") + self.connected = True + +# Test +db1 = DatabaseConnection() # Création de la connexion à la base de données +db2 = DatabaseConnection() # Pas de message - même instance +print(db1 is db2) # True +``` + +## Générateurs et yield + +### Générateurs simples +```python +def nombres_pairs(limite): + """Générateur de nombres pairs jusqu'à limite""" + for i in range(0, limite + 1, 2): + yield i + +# Utilisation +for pair in nombres_pairs(10): + print(pair) # 0, 2, 4, 6, 8, 10 + +# Les générateurs sont des itérateurs +gen = nombres_pairs(6) +print(next(gen)) # 0 +print(next(gen)) # 2 +print(next(gen)) # 4 +``` + +### Générateurs plus complexes +```python +def fibonacci_generator(): + """Générateur infini de nombres de Fibonacci""" + a, b = 0, 1 + while True: + yield a + a, b = b, a + b + +def lecture_fichier_par_chunks(nom_fichier, taille_chunk=1024): + """Lit un fichier par chunks""" + with open(nom_fichier, 'r') as f: + while True: + chunk = f.read(taille_chunk) + if not chunk: + break + yield chunk + +def pipeline_traitement(donnees): + """Pipeline de traitement avec générateurs""" + for donnee in donnees: + # Étape 1: Nettoyer + donnee_propre = donnee.strip().lower() + + # Étape 2: Filtrer + if len(donnee_propre) > 3: + # Étape 3: Transformer + yield donnee_propre.title() + +# Utilisation +donnees = [" alice ", "bob", " CHARLIE ", "ed"] +for resultat in pipeline_traitement(donnees): + print(resultat) # Alice, Charlie +``` + +### Send et communication bidirectionnelle +```python +def accumulateur(): + """Générateur qui accumule les valeurs envoyées""" + total = 0 + while True: + valeur = yield total + if valeur is not None: + total += valeur + +# Utilisation +acc = accumulateur() +next(acc) # Initialiser le générateur +print(acc.send(10)) # 10 +print(acc.send(20)) # 30 +print(acc.send(5)) # 35 +``` + +## Fonctions récursives + +### Récursion simple +```python +def factorielle(n): + """Calcule la factorielle de n""" + # Cas de base + if n <= 1: + return 1 + # Cas récursif + return n * factorielle(n - 1) + +print(factorielle(5)) # 120 + +def fibonacci_recursif(n): + """Calcule le nième nombre de Fibonacci (récursivement)""" + if n <= 1: + return n + return fibonacci_recursif(n-1) + fibonacci_recursif(n-2) +``` + +### Récursion avec mémorisation +```python +def fibonacci_memo(n, memo={}): + """Fibonacci avec mémorisation pour éviter les recalculs""" + if n in memo: + return memo[n] + + if n <= 1: + memo[n] = n + else: + memo[n] = fibonacci_memo(n-1, memo) + fibonacci_memo(n-2, memo) + + return memo[n] + +# Plus propre avec un décorateur +from functools import lru_cache + +@lru_cache(maxsize=None) +def fibonacci_cache(n): + if n <= 1: + return n + return fibonacci_cache(n-1) + fibonacci_cache(n-2) +``` + +### Récursion sur structures de données +```python +def parcourir_arbre(noeud): + """Parcourt récursivement une structure d'arbre""" + if isinstance(noeud, dict): + for cle, valeur in noeud.items(): + print(f"Clé: {cle}") + parcourir_arbre(valeur) + elif isinstance(noeud, list): + for i, element in enumerate(noeud): + print(f"Index {i}:") + parcourir_arbre(element) + else: + print(f"Valeur: {noeud}") + +# Structure imbriquée +donnees = { + "utilisateurs": [ + {"nom": "Alice", "age": 30}, + {"nom": "Bob", "infos": {"age": 25, "ville": "Paris"}} + ], + "config": { + "debug": True, + "version": "1.0" + } +} + +parcourir_arbre(donnees) +``` + +## Gestion d'erreurs dans les fonctions + +### Fonctions avec gestion d'erreurs +```python +def diviser(a, b): + """Division sécurisée avec gestion d'erreurs""" + try: + if not isinstance(a, (int, float)) or not isinstance(b, (int, float)): + raise TypeError("Les arguments doivent être des nombres") + + if b == 0: + raise ValueError("Division par zéro impossible") + + return a / b + + except TypeError as e: + print(f"Erreur de type: {e}") + return None + + except ValueError as e: + print(f"Erreur de valeur: {e}") + return None + +# Tests +print(diviser(10, 2)) # 5.0 +print(diviser(10, 0)) # Erreur de valeur: Division par zéro impossible, None +print(diviser(10, "2")) # Erreur de type: Les arguments doivent être des nombres, None +``` + +### Propagation d'erreurs +```python +def lire_fichier_securise(nom_fichier): + """Lecture sécurisée d'un fichier""" + try: + with open(nom_fichier, 'r') as f: + return f.read() + except FileNotFoundError: + raise FileNotFoundError(f"Le fichier '{nom_fichier}' n'existe pas") + except PermissionError: + raise PermissionError(f"Pas de permission pour lire '{nom_fichier}'") + except Exception as e: + raise Exception(f"Erreur inattendue lors de la lecture: {e}") + +def traiter_fichier(nom_fichier): + """Traite un fichier avec gestion d'erreurs""" + try: + contenu = lire_fichier_securise(nom_fichier) + # Traiter le contenu... + return len(contenu.split()) + except (FileNotFoundError, PermissionError) as e: + print(f"Impossible de traiter le fichier: {e}") + return 0 +``` + +## Techniques avancées + +### Fonctions partielles +```python +from functools import partial + +def multiplier(x, y): + return x * y + +# Créer une fonction partiellement appliquée +doubler = partial(multiplier, 2) +tripler = partial(multiplier, 3) + +print(doubler(5)) # 10 (équivalent à multiplier(2, 5)) +print(tripler(4)) # 12 (équivalent à multiplier(3, 4)) + +# Utile avec des fonctions complexes +def logger(niveau, message, timestamp=None): + from datetime import datetime + if timestamp is None: + timestamp = datetime.now() + print(f"[{niveau}] {timestamp}: {message}") + +# Créer des loggers spécialisés +log_error = partial(logger, "ERROR") +log_info = partial(logger, "INFO") + +log_error("Quelque chose s'est mal passé") +log_info("Opération réussie") +``` + +### Introspection de fonctions +```python +import inspect + +def exemple_fonction(a, b=10, *args, **kwargs): + """Fonction d'exemple pour l'introspection""" + return a + b + +# Informations sur la fonction +print(f"Nom: {exemple_fonction.__name__}") +print(f"Documentation: {exemple_fonction.__doc__}") +print(f"Annotations: {getattr(exemple_fonction, '__annotations__', {})}") + +# Signature avec inspect +sig = inspect.signature(exemple_fonction) +print(f"Signature: {sig}") + +for nom, param in sig.parameters.items(): + print(f"Paramètre {nom}: défaut={param.default}, kind={param.kind}") + +# Vérifier si un objet est appelable +print(callable(exemple_fonction)) # True +print(callable("string")) # False +``` + +### Annotations de type +```python +from typing import List, Dict, Optional, Union + +def calculer_moyenne(nombres: List[float]) -> float: + """Calcule la moyenne d'une liste de nombres""" + if not nombres: + raise ValueError("La liste ne peut pas être vide") + return sum(nombres) / len(nombres) + +def creer_utilisateur( + nom: str, + age: int, + email: Optional[str] = None, + preferences: Dict[str, Union[str, bool]] = None +) -> Dict[str, any]: + """Crée un dictionnaire utilisateur avec validation de types""" + if preferences is None: + preferences = {} + + return { + "nom": nom, + "age": age, + "email": email, + "preferences": preferences + } + +# Les annotations sont disponibles dans __annotations__ +print(calculer_moyenne.__annotations__) +``` + +### Métaprogrammation avec les fonctions +```python +def creer_getter_setter(nom_attribut): + """Crée dynamiquement des fonctions getter et setter""" + + def getter(self): + return getattr(self, f"_{nom_attribut}") + + def setter(self, valeur): + setattr(self, f"_{nom_attribut}", valeur) + + getter.__name__ = f"get_{nom_attribut}" + setter.__name__ = f"set_{nom_attribut}" + + return getter, setter + +class Personne: + def __init__(self, nom, age): + self._nom = nom + self._age = age + +# Ajouter dynamiquement les méthodes +get_nom, set_nom = creer_getter_setter("nom") +get_age, set_age = creer_getter_setter("age") + +Personne.get_nom = get_nom +Personne.set_nom = set_nom +Personne.get_age = get_age +Personne.set_age = set_age + +# Test +p = Personne("Alice", 30) +print(p.get_nom()) # Alice +p.set_age(31) +print(p.get_age()) # 31 +``` + +## Bonnes pratiques + +### Nommage et structure +```python +# ✓ Bon - nom descriptif +def calculer_prix_avec_taxes(prix_ht, taux_tva): + return prix_ht * (1 + taux_tva) + +# ✗ Mauvais - nom peu descriptif +def calc(p, t): + return p * (1 + t) + +# ✓ Bon - une seule responsabilité +def valider_email(email): + return "@" in email and "." in email.split("@")[1] + +def envoyer_email(destinataire, sujet, contenu): + # Logique d'envoi + pass + +# ✗ Mauvais - multiples responsabilités +def valider_et_envoyer_email(email, sujet, contenu): + # Validation ET envoi dans la même fonction + pass +``` + +### Documentation et tests +```python +def calculer_distance_euclidienne(point1, point2): + """ + Calcule la distance euclidienne entre deux points. + + Args: + point1 (tuple): Premier point (x, y) + point2 (tuple): Deuxième point (x, y) + + Returns: + float: Distance euclidienne entre les deux points + + Raises: + ValueError: Si les points ne sont pas des tuples de 2 éléments + + Examples: + >>> calculer_distance_euclidienne((0, 0), (3, 4)) + 5.0 + >>> calculer_distance_euclidienne((1, 1), (1, 1)) + 0.0 + """ + if len(point1) != 2 or len(point2) != 2: + raise ValueError("Les points doivent être des tuples de 2 éléments") + + dx = point2[0] - point1[0] + dy = point2[1] - point1[1] + return (dx**2 + dy**2)**0.5 + +# Tests intégrés avec doctest +if __name__ == "__main__": + import doctest + doctest.testmod() +``` + +### Gestion des erreurs +```python +def traiter_donnees(donnees, operation): + """ + Traite une liste de données avec une opération donnée. + + Gère les erreurs de façon appropriée. + """ + if not isinstance(donnees, list): + raise TypeError("Les données doivent être une liste") + + if not callable(operation): + raise TypeError("L'opération doit être une fonction") + + resultats = [] + erreurs = [] + + for i, donnee in enumerate(donnees): + try: + resultat = operation(donnee) + resultats.append(resultat) + except Exception as e: + erreurs.append((i, donnee, str(e))) + + return { + 'resultats': resultats, + 'erreurs': erreurs, + 'nb_succes': len(resultats), + 'nb_erreurs': len(erreurs) + } + +# Utilisation +def doubler(x): + return x * 2 + +donnees = [1, 2, "invalide", 4, None, 6] +resultat = traiter_donnees(donnees, doubler) +print(f"Succès: {resultat['nb_succes']}, Erreurs: {resultat['nb_erreurs']}") +``` + +### Performance et optimisation +```python +import time +from functools import lru_cache + +# Cache pour éviter les recalculs coûteux +@lru_cache(maxsize=128) +def operation_couteuse(n): + """Simulation d'une opération coûteuse""" + time.sleep(0.1) # Simulation + return n ** 2 + +# Générateurs pour l'efficacité mémoire +def traiter_gros_fichier(nom_fichier): + """Traite un gros fichier ligne par ligne""" + with open(nom_fichier, 'r') as f: + for numero_ligne, ligne in enumerate(f, 1): + ligne_propre = ligne.strip() + if ligne_propre: # Ignorer les lignes vides + yield numero_ligne, ligne_propre + +# Validation d'entrée efficace +def valider_parametres(**validations): + """Décorateur pour valider les paramètres d'une fonction""" + def decorateur(func): + @wraps(func) + def wrapper(*args, **kwargs): + # Obtenir la signature de la fonction + sig = inspect.signature(func) + bound = sig.bind(*args, **kwargs) + bound.apply_defaults() + + # Valider chaque paramètre + for nom_param, valeur in bound.arguments.items(): + if nom_param in validations: + validateur = validations[nom_param] + if not validateur(valeur): + raise ValueError(f"Paramètre {nom_param} invalide: {valeur}") + + return func(*args, **kwargs) + return wrapper + return decorateur + +# Utilisation +@valider_parametres( + age=lambda x: isinstance(x, int) and 0 <= x <= 150, + nom=lambda x: isinstance(x, str) and len(x.strip()) > 0 +) +def creer_personne(nom, age): + return {"nom": nom.strip(), "age": age} +``` + +## Exercices pratiques + +### Exercices de base +```python +# Exercice 1: Calculateur simple +def calculatrice(a, b, operation): + """ + Effectue une opération entre deux nombres. + + Args: + a, b (float): Les nombres à calculer + operation (str): '+', '-', '*', '/' ou '%' + + Returns: + float: Le résultat de l'opération + """ + if operation == '+': + return a + b + elif operation == '-': + return a - b + elif operation == '*': + return a * b + elif operation == '/': + if b == 0: + raise ValueError("Division par zéro") + return a / b + elif operation == '%': + if b == 0: + raise ValueError("Modulo par zéro") + return a % b + else: + raise ValueError(f"Opération '{operation}' non supportée") + +# Exercice 2: Analyse de texte +def analyser_phrase(phrase): + """Analyse une phrase et retourne des statistiques""" + mots = phrase.lower().split() + + return { + 'nb_mots': len(mots), + 'nb_caracteres': len(phrase), + 'nb_voyelles': sum(1 for c in phrase.lower() if c in 'aeiou'), + 'mot_plus_long': max(mots, key=len) if mots else '', + 'mots_uniques': len(set(mots)) + } + +# Exercice 3: Générateur de mots de passe +import random +import string + +def generer_mot_de_passe(longueur=8, inclure_maj=True, inclure_chiffres=True, inclure_speciaux=True): + """Génère un mot de passe selon les critères donnés""" + caracteres = string.ascii_lowercase + + if inclure_maj: + caracteres += string.ascii_uppercase + if inclure_chiffres: + caracteres += string.digits + if inclure_speciaux: + caracteres += "!@#$%^&*" + + return ''.join(random.choice(caracteres) for _ in range(longueur)) +``` + +### Exercices avancés +```python +# Exercice 1: Système de cache intelligent +class CacheIntelligent: + def __init__(self, taille_max=100, ttl=3600): + self.cache = {} + self.acces = {} + self.temps_creation = {} + self.taille_max = taille_max + self.ttl = ttl # Time to live en secondes + + def get(self, cle): + import time + maintenant = time.time() + + if cle in self.cache: + # Vérifier l'expiration + if maintenant - self.temps_creation[cle] > self.ttl: + self._supprimer(cle) + return None + + # Mettre à jour les statistiques d'accès + self.acces[cle] += 1 + return self.cache[cle] + + return None + + def put(self, cle, valeur): + import time + + if len(self.cache) >= self.taille_max and cle not in self.cache: + self._liberer_espace() + + self.cache[cle] = valeur + self.acces[cle] = 0 + self.temps_creation[cle] = time.time() + + def _supprimer(self, cle): + if cle in self.cache: + del self.cache[cle] + del self.acces[cle] + del self.temps_creation[cle] + + def _liberer_espace(self): + # Supprimer l'élément le moins utilisé + cle_min = min(self.acces, key=self.acces.get) + self._supprimer(cle_min) + +# Exercice 2: Pipeline de traitement de données +def creer_pipeline(*fonctions): + """Crée un pipeline de fonctions de traitement""" + def pipeline(donnee): + resultat = donnee + for fonction in fonctions: + resultat = fonction(resultat) + return resultat + + return pipeline + +# Fonctions de traitement +def nettoyer_texte(texte): + return texte.strip().lower() + +def supprimer_ponctuation(texte): + import string + return texte.translate(str.maketrans('', '', string.punctuation)) + +def diviser_en_mots(texte): + return texte.split() + +def filtrer_mots_courts(mots, longueur_min=3): + return [mot for mot in mots if len(mot) >= longueur_min] + +# Création du pipeline +pipeline_texte = creer_pipeline( + nettoyer_texte, + supprimer_ponctuation, + diviser_en_mots, + lambda mots: filtrer_mots_courts(mots, 4) +) + +# Utilisation +texte = " Bonjour! Comment allez-vous aujourd'hui? " +resultat = pipeline_texte(texte) +print(resultat) # ['bonjour', 'comment', 'allez', 'vous', 'aujourdhui'] +``` + +## Résumé + +Les fonctions sont un élément fondamental de Python qui permettent de : + +1. **Organiser le code** en blocs logiques et réutilisables +2. **Éviter la répétition** grâce à la réutilisabilité +3. **Faciliter les tests** et le débogage +4. **Améliorer la lisibilité** et la maintenabilité + +### Points clés à retenir : + +- **Syntaxe claire** : `def`, paramètres, `return` +- **Flexibilité** : `*args`, `**kwargs`, valeurs par défaut +- **Scope** : Local, Enclosing, Global, Built-in (LEGB) +- **Techniques avancées** : lambda, décorateurs, générateurs +- **Bonnes pratiques** : documentation, gestion d'erreurs, tests + +Les fonctions Python offrent une grande expressivité et flexibilité, permettant d'écrire du code élégant et efficace pour tous vos projets de data science. \ No newline at end of file diff --git a/Loop.md b/Loop.md new file mode 100644 index 0000000..9128bcb --- /dev/null +++ b/Loop.md @@ -0,0 +1,851 @@ +# Les Boucles en Python - Du Débutant à l'Expert + +## Table des matières +1. [Introduction aux boucles](#introduction-aux-boucles) +2. [Boucle for - Niveau débutant](#boucle-for---niveau-débutant) +3. [Boucle while - Niveau débutant](#boucle-while---niveau-débutant) +4. [Contrôle de flux - break et continue](#contrôle-de-flux---break-et-continue) +5. [Boucles imbriquées](#boucles-imbriquées) +6. [Techniques intermédiaires](#techniques-intermédiaires) +7. [List Comprehensions - Niveau avancé](#list-comprehensions---niveau-avancé) +8. [Générateurs et itérateurs](#générateurs-et-itérateurs) +9. [Techniques expertes](#techniques-expertes) +10. [Optimisation et performance](#optimisation-et-performance) +11. [Patterns avancés et cas d'usage](#patterns-avancés-et-cas-dusage) + +## Introduction aux boucles + +Les boucles permettent de répéter des instructions plusieurs fois. Python offre deux types principaux de boucles : +- **for** : pour itérer sur des séquences +- **while** : pour répéter tant qu'une condition est vraie + +## Boucle for - Niveau débutant + +### Syntaxe de base +```python +# Structure générale +for element in sequence: + # Instructions à répéter + pass +``` + +### Itération sur différentes structures + +#### Listes +```python +fruits = ["pomme", "banane", "orange"] + +# Itération simple +for fruit in fruits: + print(f"J'aime les {fruit}s") + +# Avec indices +for i in range(len(fruits)): + print(f"Fruit {i}: {fruits[i]}") + +# Avec enumerate() (plus pythonique) +for index, fruit in enumerate(fruits): + print(f"Fruit {index}: {fruit}") + +# Avec valeur de départ pour l'index +for index, fruit in enumerate(fruits, start=1): + print(f"Fruit n°{index}: {fruit}") +``` + +#### Chaînes de caractères +```python +mot = "Python" + +# Caractère par caractère +for lettre in mot: + print(lettre) + +# Avec positions +for i, lettre in enumerate(mot): + print(f"Position {i}: {lettre}") +``` + +#### Dictionnaires +```python +personne = {"nom": "Alice", "age": 30, "ville": "Paris"} + +# Itération sur les clés +for cle in personne: + print(f"{cle}: {personne[cle]}") + +# Itération sur les clés (explicite) +for cle in personne.keys(): + print(cle) + +# Itération sur les valeurs +for valeur in personne.values(): + print(valeur) + +# Itération sur les paires clé-valeur +for cle, valeur in personne.items(): + print(f"{cle} = {valeur}") +``` + +#### Fonction range() +```python +# Range de base +for i in range(5): # 0, 1, 2, 3, 4 + print(i) + +# Avec début et fin +for i in range(2, 8): # 2, 3, 4, 5, 6, 7 + print(i) + +# Avec pas (step) +for i in range(0, 10, 2): # 0, 2, 4, 6, 8 + print(i) + +# Range décroissant +for i in range(10, 0, -1): # 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 + print(i) +``` + +## Boucle while - Niveau débutant + +### Syntaxe de base +```python +# Structure générale +while condition: + # Instructions à répéter + pass +``` + +### Exemples pratiques +```python +# Compteur simple +compteur = 0 +while compteur < 5: + print(f"Compteur: {compteur}") + compteur += 1 + +# Saisie utilisateur avec validation +age = -1 +while age < 0 or age > 150: + age = int(input("Entrez un âge valide (0-150): ")) +print(f"Âge saisi: {age}") + +# Boucle infinie contrôlée +while True: + reponse = input("Voulez-vous continuer? (o/n): ") + if reponse.lower() in ['n', 'non']: + break + print("On continue!") + +# Lecture de fichier ligne par ligne +with open("fichier.txt", "r") as f: + ligne = f.readline() + while ligne: + print(ligne.strip()) + ligne = f.readline() +``` + +## Contrôle de flux - break et continue + +### break - Sortir de la boucle +```python +# Recherche d'un élément +nombres = [1, 3, 7, 12, 18, 25] +cible = 12 + +for nombre in nombres: + if nombre == cible: + print(f"Nombre {cible} trouvé!") + break + print(f"Vérification de {nombre}") +``` + +### continue - Passer à l'itération suivante +```python +# Ignorer les nombres pairs +for i in range(10): + if i % 2 == 0: + continue # Passe à l'itération suivante + print(f"Nombre impair: {i}") + +# Filtrer les valeurs invalides +temperatures = [20.5, -999, 22.3, -999, 18.7, 25.1] +for temp in temperatures: + if temp == -999: # Valeur d'erreur + continue + print(f"Température valide: {temp}°C") +``` + +### else avec les boucles +```python +# else avec for - exécuté si la boucle se termine normalement +nombres = [1, 3, 5, 7, 9] +for nombre in nombres: + if nombre % 2 == 0: + print("Nombre pair trouvé!") + break +else: + print("Aucun nombre pair trouvé") + +# else avec while +compteur = 0 +while compteur < 3: + print(f"Tentative {compteur + 1}") + compteur += 1 +else: + print("Toutes les tentatives terminées") +``` + +## Boucles imbriquées + +### Matrices et tableaux 2D +```python +# Création d'une matrice +matrice = [ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9] +] + +# Parcours complet +for ligne in matrice: + for element in ligne: + print(element, end=" ") + print() # Nouvelle ligne + +# Avec indices +for i in range(len(matrice)): + for j in range(len(matrice[i])): + print(f"matrice[{i}][{j}] = {matrice[i][j]}") +``` + +### Génération de patterns +```python +# Triangle d'étoiles +for i in range(1, 6): + for j in range(i): + print("*", end="") + print() + +# Table de multiplication +for i in range(1, 11): + for j in range(1, 11): + print(f"{i*j:4d}", end="") + print() + +# Échiquier +for i in range(8): + for j in range(8): + if (i + j) % 2 == 0: + print("⬜", end="") + else: + print("⬛", end="") + print() +``` + +## Techniques intermédiaires + +### Unpacking dans les boucles +```python +# Tuples dans une liste +points = [(1, 2), (3, 4), (5, 6)] +for x, y in points: + print(f"Point: ({x}, {y})") + +# Dictionnaires avec items() +scores = {"Alice": 95, "Bob": 87, "Charlie": 92} +for nom, score in scores.items(): + if score >= 90: + print(f"{nom}: Excellent ({score})") +``` + +### zip() pour itérer sur plusieurs séquences +```python +noms = ["Alice", "Bob", "Charlie"] +ages = [25, 30, 35] +villes = ["Paris", "Lyon", "Marseille"] + +# Itération simultanée +for nom, age, ville in zip(noms, ages, villes): + print(f"{nom}, {age} ans, habite à {ville}") + +# zip avec différentes longueurs +from itertools import zip_longest + +liste1 = [1, 2, 3] +liste2 = ['a', 'b', 'c', 'd', 'e'] + +for num, lettre in zip_longest(liste1, liste2, fillvalue=0): + print(f"{num} - {lettre}") +``` + +### reversed() et sorted() +```python +# Itération en ordre inverse +for i in reversed(range(5)): # 4, 3, 2, 1, 0 + print(i) + +fruits = ["pomme", "banane", "orange"] +for fruit in reversed(fruits): + print(fruit) + +# Itération triée +nombres = [3, 1, 4, 1, 5, 9, 2, 6] +for nombre in sorted(nombres): + print(nombre) + +# Tri personnalisé +mots = ["python", "java", "c", "javascript"] +for mot in sorted(mots, key=len): # Tri par longueur + print(mot) +``` + +## List Comprehensions - Niveau avancé + +### Syntaxe de base +```python +# Structure générale +# [expression for item in iterable if condition] + +# Transformation simple +nombres = [1, 2, 3, 4, 5] +carres = [x**2 for x in nombres] # [1, 4, 9, 16, 25] + +# Avec condition +pairs = [x for x in range(10) if x % 2 == 0] # [0, 2, 4, 6, 8] + +# Transformation avec condition +positifs_carres = [x**2 for x in range(-5, 6) if x > 0] # [1, 4, 9, 16, 25] +``` + +### Exemples avancés +```python +# Manipulation de chaînes +mots = ["python", "java", "javascript", "c"] +majuscules = [mot.upper() for mot in mots if len(mot) > 3] +# ['PYTHON', 'JAVA', 'JAVASCRIPT'] + +# Aplatissement de listes +matrice = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] +aplati = [element for ligne in matrice for element in ligne] +# [1, 2, 3, 4, 5, 6, 7, 8, 9] + +# Dict comprehension +scores = [85, 92, 78, 96, 87] +resultats = {f"Étudiant_{i}": score for i, score in enumerate(scores, 1)} +# {'Étudiant_1': 85, 'Étudiant_2': 92, ...} + +# Set comprehension +texte = "Hello World" +voyelles_uniques = {char.lower() for char in texte if char.lower() in 'aeiou'} +# {'e', 'o'} +``` + +### Comprehensions imbriquées +```python +# Matrice avec list comprehension +matrice = [[i * j for j in range(1, 4)] for i in range(1, 4)] +# [[1, 2, 3], [2, 4, 6], [3, 6, 9]] + +# Filtrage complexe +donnees = [[1, 2, 3], [4, 5], [6, 7, 8, 9]] +pairs_seulement = [[x for x in ligne if x % 2 == 0] for ligne in donnees] +# [[2], [4], [6, 8]] + +# Transformation de dictionnaire +personne = {"nom": "alice", "ville": "paris", "age": 30} +maj = {k: v.title() if isinstance(v, str) else v for k, v in personne.items()} +# {'nom': 'Alice', 'ville': 'Paris', 'age': 30} +``` + +## Générateurs et itérateurs + +### Générateurs avec yield +```python +def nombres_pairs(limit): + """Générateur de nombres pairs jusqu'à limit""" + for i in range(0, limit + 1, 2): + yield i + +# Utilisation +for pair in nombres_pairs(10): + print(pair) # 0, 2, 4, 6, 8, 10 + +# Générateur infini +def fibonacci(): + """Générateur infini de nombres de Fibonacci""" + a, b = 0, 1 + while True: + yield a + a, b = b, a + b + +# Utilisation avec limite +fib = fibonacci() +for _ in range(10): + print(next(fib)) +``` + +### Generator expressions +```python +# Similaire aux list comprehensions mais avec des parenthèses +carres_gen = (x**2 for x in range(1000000)) # Ne prend pas de mémoire immédiatement + +# Utilisation mémoire efficace +somme_carres = sum(x**2 for x in range(1000)) + +# Filtrage avec générateurs +fichier_lignes = (ligne.strip() for ligne in open('fichier.txt') + if not ligne.startswith('#')) +``` + +### Itérateurs personnalisés +```python +class CompteurPersonnalise: + def __init__(self, debut, fin): + self.courant = debut + self.fin = fin + + def __iter__(self): + return self + + def __next__(self): + if self.courant < self.fin: + self.courant += 1 + return self.courant - 1 + else: + raise StopIteration + +# Utilisation +for i in CompteurPersonnalise(0, 5): + print(i) # 0, 1, 2, 3, 4 +``` + +## Techniques expertes + +### itertools - La boîte à outils des itérateurs +```python +import itertools + +# Produit cartésien +couleurs = ['rouge', 'vert', 'bleu'] +tailles = ['S', 'M', 'L'] +combinaisons = list(itertools.product(couleurs, tailles)) +# [('rouge', 'S'), ('rouge', 'M'), ..., ('bleu', 'L')] + +# Combinaisons +from itertools import combinations, permutations + +lettres = ['A', 'B', 'C', 'D'] +combos = list(combinations(lettres, 2)) # Toutes les paires +perms = list(permutations(lettres, 2)) # Toutes les permutations + +# Groupement +from itertools import groupby + +donnees = [1, 1, 2, 2, 2, 3, 1, 1] +for cle, groupe in groupby(donnees): + print(f"{cle}: {list(groupe)}") + +# Cycle infini +from itertools import cycle, islice + +couleurs_cycle = cycle(['rouge', 'vert', 'bleu']) +premieres_10 = list(islice(couleurs_cycle, 10)) +# ['rouge', 'vert', 'bleu', 'rouge', 'vert', 'bleu', 'rouge', 'vert', 'bleu', 'rouge'] + +# Chain - Aplatir plusieurs itérables +from itertools import chain + +liste1 = [1, 2, 3] +liste2 = [4, 5, 6] +liste3 = [7, 8, 9] +aplati = list(chain(liste1, liste2, liste3)) # [1, 2, 3, 4, 5, 6, 7, 8, 9] +``` + +### Techniques de traitement de données +```python +# Traitement par chunks +def traiter_par_chunks(iterable, taille_chunk): + """Traite les données par blocs de taille fixe""" + it = iter(iterable) + while True: + chunk = list(itertools.islice(it, taille_chunk)) + if not chunk: + break + yield chunk + +# Utilisation +grandes_donnees = range(25) +for chunk in traiter_par_chunks(grandes_donnees, 5): + print(f"Traitement du chunk: {chunk}") + +# Pipeline de transformation +def pipeline_traitement(donnees): + """Pipeline de traitement avec générateurs""" + # Étape 1: Filtrer les nombres positifs + positifs = (x for x in donnees if x > 0) + + # Étape 2: Mettre au carré + carres = (x**2 for x in positifs) + + # Étape 3: Garder seulement ceux < 100 + petits = (x for x in carres if x < 100) + + return petits + +donnees = [-5, -2, 1, 3, 8, 12, -1, 4] +resultat = list(pipeline_traitement(donnees)) +print(resultat) # [1, 9, 64, 16] +``` + +### Programmation fonctionnelle avec boucles +```python +# map, filter, reduce avec boucles +from functools import reduce + +nombres = [1, 2, 3, 4, 5] + +# Équivalent de map +carres_map = list(map(lambda x: x**2, nombres)) +carres_loop = [x**2 for x in nombres] + +# Équivalent de filter +pairs_filter = list(filter(lambda x: x % 2 == 0, nombres)) +pairs_loop = [x for x in nombres if x % 2 == 0] + +# Équivalent de reduce +somme_reduce = reduce(lambda x, y: x + y, nombres) +somme_loop = sum(nombres) + +# Approche hybride +def traitement_avance(donnees, transformations): + """Applique une série de transformations""" + resultat = donnees + for transformation in transformations: + if callable(transformation): + resultat = [transformation(x) for x in resultat] + else: # C'est un filtre (prédicat) + resultat = [x for x in resultat if transformation(x)] + return resultat +``` + +## Optimisation et performance + +### Comparaison de performances +```python +import time + +def mesurer_temps(func, *args): + """Mesure le temps d'exécution d'une fonction""" + start = time.time() + resultat = func(*args) + end = time.time() + print(f"{func.__name__}: {end - start:.4f}s") + return resultat + +# Test avec différentes approches +n = 100000 + +def avec_append(): + result = [] + for i in range(n): + result.append(i**2) + return result + +def avec_list_comp(): + return [i**2 for i in range(n)] + +def avec_map(): + return list(map(lambda x: x**2, range(n))) + +# Comparaison +# mesurer_temps(avec_append) # Plus lent +# mesurer_temps(avec_list_comp) # Rapide +# mesurer_temps(avec_map) # Intermédiaire +``` + +### Bonnes pratiques pour les performances +```python +# 1. Éviter les lookups répétés dans les boucles +# Mauvais +donnees = [1, 2, 3, 4, 5] +for i in range(len(donnees)): + print(math.sqrt(donnees[i])) # lookup de math.sqrt à chaque itération + +# Bon +import math +sqrt = math.sqrt # Une seule fois +for valeur in donnees: + print(sqrt(valeur)) + +# 2. Utiliser des générateurs pour de gros volumes +# Mauvais - charge tout en mémoire +gros_carres = [x**2 for x in range(1000000)] + +# Bon - générateur +gros_carres_gen = (x**2 for x in range(1000000)) + +# 3. Préférer les méthodes built-in +# Moins efficace +total = 0 +for x in nombres: + total += x + +# Plus efficace +total = sum(nombres) +``` + +## Patterns avancés et cas d'usage + +### State machines avec boucles +```python +class MachineEtats: + def __init__(self): + self.etat = "DEBUT" + self.donnees = [] + + def traiter(self, entrees): + for entree in entrees: + if self.etat == "DEBUT": + if entree == "START": + self.etat = "TRAITEMENT" + print("Démarrage du traitement") + + elif self.etat == "TRAITEMENT": + if entree == "DATA": + self.donnees.append(len(self.donnees) + 1) + print(f"Données ajoutées: {self.donnees[-1]}") + elif entree == "END": + self.etat = "FIN" + print("Fin du traitement") + + elif self.etat == "FIN": + print("Traitement terminé") + break + +# Utilisation +machine = MachineEtats() +commandes = ["START", "DATA", "DATA", "DATA", "END"] +machine.traiter(commandes) +``` + +### Parsing et analyse de données +```python +def analyser_logs(fichier_log): + """Analyse un fichier de logs avec différents patterns""" + stats = { + 'erreurs': 0, + 'warnings': 0, + 'info': 0, + 'ip_addresses': set(), + 'codes_http': {} + } + + with open(fichier_log, 'r') as f: + for ligne in f: + ligne = ligne.strip() + + # Détecter le niveau de log + if 'ERROR' in ligne: + stats['erreurs'] += 1 + elif 'WARNING' in ligne: + stats['warnings'] += 1 + elif 'INFO' in ligne: + stats['info'] += 1 + + # Extraire les adresses IP (pattern simple) + import re + ip_pattern = r'\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b' + ips = re.findall(ip_pattern, ligne) + stats['ip_addresses'].update(ips) + + # Extraire les codes HTTP + code_pattern = r' (\d{3}) ' + codes = re.findall(code_pattern, ligne) + for code in codes: + stats['codes_http'][code] = stats['codes_http'].get(code, 0) + 1 + + return stats +``` + +### Worker pools et traitement parallèle +```python +from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor +import time + +def traitement_intensif(donnee): + """Simule un traitement coûteux""" + time.sleep(0.1) # Simulation + return donnee ** 2 + +def traiter_avec_workers(donnees, nb_workers=4): + """Traite les données en parallèle""" + with ThreadPoolExecutor(max_workers=nb_workers) as executor: + # Soumission de toutes les tâches + futures = {executor.submit(traitement_intensif, donnee): donnee + for donnee in donnees} + + # Collecte des résultats au fur et à mesure + resultats = {} + for future in concurrent.futures.as_completed(futures): + donnee_originale = futures[future] + try: + resultat = future.result() + resultats[donnee_originale] = resultat + except Exception as e: + print(f"Erreur pour {donnee_originale}: {e}") + + return resultats + +# Utilisation +# donnees = list(range(20)) +# resultats = traiter_avec_workers(donnees) +``` + +## Exercices pratiques par niveau + +### Niveau Débutant +```python +# Exercice 1: Table de multiplication +def table_multiplication(n): + for i in range(1, 11): + print(f"{n} x {i} = {n * i}") + +# Exercice 2: Comptage de caractères +def compter_caracteres(texte): + compteurs = {} + for char in texte.lower(): + if char.isalpha(): + compteurs[char] = compteurs.get(char, 0) + 1 + return compteurs + +# Exercice 3: Recherche dans une liste +def trouver_max_min(liste): + if not liste: + return None, None + + maximum = minimum = liste[0] + for nombre in liste[1:]: + if nombre > maximum: + maximum = nombre + if nombre < minimum: + minimum = nombre + + return maximum, minimum +``` + +### Niveau Intermédiaire +```python +# Exercice 1: Validation d'email +def valider_emails(emails): + valides = [] + invalides = [] + + for email in emails: + if '@' in email and '.' in email.split('@')[1]: + valides.append(email) + else: + invalides.append(email) + + return valides, invalides + +# Exercice 2: Analyse de ventes +def analyser_ventes(ventes_data): + # ventes_data: [{"produit": "A", "quantite": 10, "prix": 15.99}, ...] + stats = {} + + for vente in ventes_data: + produit = vente["produit"] + if produit not in stats: + stats[produit] = {"quantite_totale": 0, "revenus": 0} + + stats[produit]["quantite_totale"] += vente["quantite"] + stats[produit]["revenus"] += vente["quantite"] * vente["prix"] + + return stats + +# Exercice 3: Générateur de mots de passe +import random +import string + +def generer_mots_de_passe(nb_passwords, longueur=8): + caracteres = string.ascii_letters + string.digits + "!@#$%^&*" + + for _ in range(nb_passwords): + password = ''.join(random.choice(caracteres) for _ in range(longueur)) + yield password +``` + +### Niveau Avancé +```python +# Exercice 1: Cache LRU simple +class LRUCache: + def __init__(self, capacite): + self.capacite = capacite + self.cache = {} + self.ordre = [] + + def get(self, cle): + if cle in self.cache: + # Mettre à jour l'ordre + self.ordre.remove(cle) + self.ordre.append(cle) + return self.cache[cle] + return None + + def put(self, cle, valeur): + if cle in self.cache: + self.ordre.remove(cle) + elif len(self.cache) >= self.capacite: + # Supprimer l'élément le moins récemment utilisé + ancien = self.ordre.pop(0) + del self.cache[ancien] + + self.cache[cle] = valeur + self.ordre.append(cle) + +# Exercice 2: Analyseur de performance de boucles +import time +import memory_profiler + +def analyser_performance(func, *args, **kwargs): + # Mesure du temps + start_time = time.time() + + # Mesure de la mémoire (nécessite memory_profiler) + start_memory = memory_profiler.memory_usage()[0] + + resultat = func(*args, **kwargs) + + end_time = time.time() + end_memory = memory_profiler.memory_usage()[0] + + return { + 'resultat': resultat, + 'temps': end_time - start_time, + 'memoire': end_memory - start_memory + } +``` + +## Résumé et bonnes pratiques + +### Principes fondamentaux +1. **Choisir la bonne boucle** : `for` pour les séquences, `while` pour les conditions +2. **Préférer les itérateurs** aux indices quand possible +3. **Utiliser les list comprehensions** pour les transformations simples +4. **Exploiter les générateurs** pour l'efficacité mémoire +5. **Maîtriser itertools** pour les opérations complexes + +### Optimisations clés +- Éviter les lookups répétés dans les boucles +- Utiliser les built-ins (`sum()`, `max()`, `min()`) +- Préférer les générateurs pour de gros volumes +- Considérer le parallélisme pour les tâches intensives + +### Patterns à retenir +- `enumerate()` pour les indices +- `zip()` pour les itérations simultanées +- `itertools` pour les opérations avancées +- Comprehensions pour la lisibilité +- Générateurs pour l'efficacité + +La maîtrise des boucles en Python est essentielle pour écrire du code efficace et élégant. Ces concepts vous accompagneront de vos premiers pas jusqu'aux projets les plus complexes en data science. \ No newline at end of file diff --git a/Module.md b/Module.md new file mode 100644 index 0000000..5867636 --- /dev/null +++ b/Module.md @@ -0,0 +1,1277 @@ +# Les Modules en Python - Guide Complet + +## Table des matières +1. [Introduction aux modules](#introduction-aux-modules) +2. [Importation de modules](#importation-de-modules) +3. [Création de modules personnalisés](#création-de-modules-personnalisés) +4. [Packages et organisation](#packages-et-organisation) +5. [Modules de la bibliothèque standard](#modules-de-la-bibliothèque-standard) +6. [Gestion des chemins et sys.path](#gestion-des-chemins-et-syspath) +7. [Modules et namespaces](#modules-et-namespaces) +8. [Installation et gestion de modules externes](#installation-et-gestion-de-modules-externes) +9. [Bonnes pratiques](#bonnes-pratiques) +10. [Modules avancés pour la Data Science](#modules-avancés-pour-la-data-science) + +## Introduction aux modules + +Un **module** en Python est un fichier contenant du code Python (variables, fonctions, classes) qui peut être importé et utilisé dans d'autres programmes. Les modules permettent de : + +- **Organiser le code** en unités logiques +- **Réutiliser** du code dans plusieurs projets +- **Éviter la pollution** de l'espace de noms +- **Faciliter la maintenance** et les tests + +### Types de modules +```python +# 1. Modules built-in (intégrés) - écrits en C +import sys +import os + +# 2. Modules de la bibliothèque standard - écrits en Python +import math +import random +import datetime + +# 3. Modules externes - installés avec pip +import numpy +import pandas +import requests + +# 4. Modules personnalisés - créés par vous +import mon_module +``` + +## Importation de modules + +### Import basique +```python +# Importer tout le module +import math +resultat = math.sqrt(16) # Utilisation avec le nom du module +print(math.pi) + +# Importer des éléments spécifiques +from math import sqrt, pi, cos +resultat = sqrt(16) # Utilisation directe sans préfixe +print(pi) + +# Importer tout le contenu (à éviter généralement) +from math import * +resultat = sqrt(16) # Tous les éléments sont disponibles directement +``` + +### Alias d'importation +```python +# Alias de module - très courant en data science +import numpy as np +import pandas as pd +import matplotlib.pyplot as plt + +array = np.array([1, 2, 3, 4]) +df = pd.DataFrame({'A': [1, 2], 'B': [3, 4]}) + +# Alias pour des éléments spécifiques +from datetime import datetime as dt +from collections import defaultdict as dd + +maintenant = dt.now() +mon_dict = dd(list) +``` + +### Import conditionnel +```python +# Vérifier la disponibilité d'un module +try: + import numpy as np + NUMPY_AVAILABLE = True +except ImportError: + NUMPY_AVAILABLE = False + print("NumPy n'est pas installé") + +def calculer_moyenne(liste): + if NUMPY_AVAILABLE: + return np.mean(liste) + else: + return sum(liste) / len(liste) + +# Import avec fallback +try: + from collections import OrderedDict +except ImportError: + # Fallback pour les versions Python < 2.7 + OrderedDict = dict +``` + +### Import dynamique +```python +# Importation à l'exécution +import importlib + +nom_module = "math" +module = importlib.import_module(nom_module) +print(module.sqrt(16)) + +# Import basé sur une condition +import sys + +if sys.version_info >= (3, 8): + from typing import TypedDict +else: + try: + from typing_extensions import TypedDict + except ImportError: + # Fallback pour les anciennes versions + TypedDict = dict +``` + +## Création de modules personnalisés + +### Module simple +Créez un fichier `utils.py` : +```python +# utils.py +""" +Module utilitaire avec des fonctions communes +""" + +# Variable de module +VERSION = "1.0.0" +AUTEUR = "Votre Nom" + +# Fonction simple +def saluer(nom): + """Salue une personne""" + return f"Bonjour {nom}!" + +def calculer_moyenne(nombres): + """Calcule la moyenne d'une liste de nombres""" + if not nombres: + raise ValueError("La liste ne peut pas être vide") + return sum(nombres) / len(nombres) + +# Classe dans un module +class Compteur: + """Classe pour compter des éléments""" + def __init__(self): + self.valeur = 0 + + def incrementer(self): + self.valeur += 1 + return self.valeur + + def decrementer(self): + self.valeur -= 1 + return self.valeur + +# Code exécuté à l'importation +print(f"Module utils version {VERSION} chargé") + +# Code exécuté seulement si le module est lancé directement +if __name__ == "__main__": + print("Tests du module utils:") + print(saluer("Alice")) + print(calculer_moyenne([1, 2, 3, 4, 5])) + + c = Compteur() + print(c.incrementer()) + print(c.incrementer()) +``` + +### Utilisation du module +```python +# main.py +import utils + +# Utiliser les fonctions du module +message = utils.saluer("Bob") +print(message) + +moyenne = utils.calculer_moyenne([10, 20, 30]) +print(f"Moyenne: {moyenne}") + +# Utiliser la classe +compteur = utils.Compteur() +print(compteur.incrementer()) + +# Accéder aux variables du module +print(f"Version: {utils.VERSION}") +print(f"Auteur: {utils.AUTEUR}") +``` + +### Module avec configuration +Créez un fichier `config.py` : +```python +# config.py +"""Module de configuration pour l'application""" + +import os +from pathlib import Path + +# Configuration de base +DEBUG = os.getenv('DEBUG', 'False').lower() == 'true' +VERSION = "1.2.3" + +# Chemins +BASE_DIR = Path(__file__).parent +DATA_DIR = BASE_DIR / "data" +LOGS_DIR = BASE_DIR / "logs" + +# Configuration de base de données +DATABASE = { + 'host': os.getenv('DB_HOST', 'localhost'), + 'port': int(os.getenv('DB_PORT', '5432')), + 'name': os.getenv('DB_NAME', 'myapp'), + 'user': os.getenv('DB_USER', 'user'), + 'password': os.getenv('DB_PASSWORD', ''), +} + +# Configuration des logs +LOGGING_CONFIG = { + 'level': 'DEBUG' if DEBUG else 'INFO', + 'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s', + 'file': LOGS_DIR / 'app.log' +} + +# Fonction d'initialisation +def initialiser_dossiers(): + """Crée les dossiers nécessaires s'ils n'existent pas""" + DATA_DIR.mkdir(exist_ok=True) + LOGS_DIR.mkdir(exist_ok=True) + +# Auto-initialisation +initialiser_dossiers() +``` + +### Module avec classes métier +Créez un fichier `models.py` : +```python +# models.py +"""Modèles de données pour l'application""" + +from datetime import datetime +from typing import List, Optional + +class Personne: + """Représente une personne""" + + def __init__(self, nom: str, prenom: str, age: int): + self.nom = nom + self.prenom = prenom + self.age = age + self.date_creation = datetime.now() + + @property + def nom_complet(self) -> str: + return f"{self.prenom} {self.nom}" + + def est_majeur(self) -> bool: + return self.age >= 18 + + def __str__(self) -> str: + return f"{self.nom_complet} ({self.age} ans)" + + def __repr__(self) -> str: + return f"Personne('{self.nom}', '{self.prenom}', {self.age})" + +class Groupe: + """Représente un groupe de personnes""" + + def __init__(self, nom: str): + self.nom = nom + self.membres: List[Personne] = [] + + def ajouter_membre(self, personne: Personne): + if personne not in self.membres: + self.membres.append(personne) + + def supprimer_membre(self, personne: Personne): + if personne in self.membres: + self.membres.remove(personne) + + def obtenir_moyenne_age(self) -> float: + if not self.membres: + return 0.0 + return sum(p.age for p in self.membres) / len(self.membres) + + def filtrer_majeurs(self) -> List[Personne]: + return [p for p in self.membres if p.est_majeur()] + + def __len__(self) -> int: + return len(self.membres) + + def __iter__(self): + return iter(self.membres) +``` + +## Packages et organisation + +### Création d'un package +Structure de dossiers : +``` +mon_package/ +│ +├── __init__.py # Indique que c'est un package +├── core.py # Module principal +├── utils.py # Utilitaires +├── exceptions.py # Exceptions personnalisées +│ +└── data/ # Sous-package + ├── __init__.py + ├── loader.py # Chargement de données + └── processor.py # Traitement de données +``` + +### Fichier __init__.py +```python +# mon_package/__init__.py +""" +Mon Package - Outils de traitement de données +""" + +# Version du package +__version__ = "1.0.0" +__author__ = "Votre Nom" + +# Importations pour faciliter l'utilisation +from .core import ProcesseurPrincipal, analyser_donnees +from .utils import nettoyer_donnees, valider_entrees +from .exceptions import ErreurDonnees, ErreurValidation + +# Configuration du package +from .core import configurer_package + +# Variables publiques (ce qui sera importé avec from package import *) +__all__ = [ + 'ProcesseurPrincipal', + 'analyser_donnees', + 'nettoyer_donnees', + 'valider_entrees', + 'ErreurDonnees', + 'ErreurValidation', + 'configurer_package' +] + +# Initialisation du package +print(f"Chargement de {__name__} version {__version__}") +``` + +### Sous-packages +```python +# mon_package/data/__init__.py +"""Sous-package pour la gestion des données""" + +from .loader import ChargerCSV, ChargerJSON +from .processor import ProcesseurDonnees + +__all__ = ['ChargerCSV', 'ChargerJSON', 'ProcesseurDonnees'] + +# mon_package/data/loader.py +"""Module de chargement de données""" + +import json +import csv +from pathlib import Path +from typing import List, Dict, Any + +class ChargerCSV: + """Classe pour charger des fichiers CSV""" + + @staticmethod + def charger(chemin_fichier: Path) -> List[Dict[str, Any]]: + """Charge un fichier CSV et retourne une liste de dictionnaires""" + with open(chemin_fichier, 'r', encoding='utf-8') as f: + reader = csv.DictReader(f) + return list(reader) + +class ChargerJSON: + """Classe pour charger des fichiers JSON""" + + @staticmethod + def charger(chemin_fichier: Path) -> Dict[str, Any]: + """Charge un fichier JSON et retourne un dictionnaire""" + with open(chemin_fichier, 'r', encoding='utf-8') as f: + return json.load(f) +``` + +### Utilisation des packages +```python +# Utilisation du package complet +import mon_package + +# Utiliser les éléments importés dans __init__.py +processeur = mon_package.ProcesseurPrincipal() +donnees_propres = mon_package.nettoyer_donnees(donnees_brutes) + +# Import spécifique depuis un sous-package +from mon_package.data import ChargerCSV +from mon_package.data.processor import ProcesseurDonnees + +# Utilisation +chargeur = ChargerCSV() +donnees = chargeur.charger(Path("data.csv")) + +# Import avec alias +from mon_package.data import loader as data_loader +donnees_json = data_loader.ChargerJSON.charger(Path("config.json")) +``` + +## Modules de la bibliothèque standard + +### Modules essentiels pour la data science + +#### os et pathlib - Gestion du système de fichiers +```python +import os +from pathlib import Path + +# os - Interface avec le système d'exploitation +print(f"Répertoire courant: {os.getcwd()}") +print(f"Variables d'environnement: {os.environ.get('HOME')}") + +# Créer un dossier +os.makedirs("data/processed", exist_ok=True) + +# pathlib - Approche orientée objet (recommandée) +projet = Path.cwd() +dossier_data = projet / "data" +fichier_config = dossier_data / "config.json" + +# Vérifications +if dossier_data.exists(): + print(f"Le dossier data existe: {dossier_data.is_dir()}") + +# Itération sur les fichiers +for fichier in dossier_data.glob("*.csv"): + print(f"Fichier CSV trouvé: {fichier.name}") +``` + +#### datetime - Gestion des dates et heures +```python +from datetime import datetime, date, time, timedelta +import calendar + +# Dates et heures courantes +maintenant = datetime.now() +aujourd_hui = date.today() +heure_courante = datetime.now().time() + +# Création de dates spécifiques +noel = date(2024, 12, 25) +reunion = datetime(2024, 3, 15, 14, 30) + +# Opérations sur les dates +dans_une_semaine = aujourd_hui + timedelta(days=7) +il_y_a_un_mois = maintenant - timedelta(days=30) + +# Formatage +date_formatee = maintenant.strftime("%d/%m/%Y %H:%M:%S") +print(f"Date formatée: {date_formatee}") + +# Parsing +date_depuis_string = datetime.strptime("2024-03-15 14:30", "%Y-%m-%d %H:%M") + +# Utilitaires +print(f"Nombre de jours en mars 2024: {calendar.monthrange(2024, 3)[1]}") +``` + +#### collections - Structures de données avancées +```python +from collections import defaultdict, Counter, deque, namedtuple, OrderedDict + +# defaultdict - Dictionnaire avec valeurs par défaut +stats = defaultdict(int) +texte = "hello world" +for char in texte: + stats[char] += 1 # Pas besoin de vérifier si la clé existe + +# Counter - Comptage d'éléments +mots = ["python", "java", "python", "c++", "python"] +compteur = Counter(mots) +print(compteur.most_common(2)) # [('python', 3), ('java', 1)] + +# deque - File double (efficace pour les ajouts/suppressions aux extrémités) +file = deque([1, 2, 3]) +file.appendleft(0) # [0, 1, 2, 3] +file.append(4) # [0, 1, 2, 3, 4] +element = file.popleft() # 0 + +# namedtuple - Tuple avec des champs nommés +Point = namedtuple('Point', ['x', 'y']) +p1 = Point(1, 2) +print(f"Coordonnées: x={p1.x}, y={p1.y}") +``` + +#### itertools - Outils pour les itérations +```python +import itertools + +# Combinaisons et permutations +lettres = ['A', 'B', 'C'] +combinaisons = list(itertools.combinations(lettres, 2)) +# [('A', 'B'), ('A', 'C'), ('B', 'C')] + +permutations = list(itertools.permutations(lettres, 2)) +# [('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'C'), ('C', 'A'), ('C', 'B')] + +# Produit cartésien +couleurs = ['rouge', 'vert'] +tailles = ['S', 'M', 'L'] +produits = list(itertools.product(couleurs, tailles)) +# [('rouge', 'S'), ('rouge', 'M'), ('rouge', 'L'), ('vert', 'S'), ('vert', 'M'), ('vert', 'L')] + +# Groupement +donnees = [1, 1, 2, 2, 2, 3, 1, 1] +groupes = [(k, list(g)) for k, g in itertools.groupby(donnees)] +# [(1, [1, 1]), (2, [2, 2, 2]), (3, [3]), (1, [1, 1])] + +# Cycles et répétitions +cycle_couleurs = itertools.cycle(['rouge', 'vert', 'bleu']) +premieres_10 = list(itertools.islice(cycle_couleurs, 10)) + +# Accumulation +nombres = [1, 2, 3, 4, 5] +cumul = list(itertools.accumulate(nombres)) # [1, 3, 6, 10, 15] +``` + +#### json et csv - Traitement de données +```python +import json +import csv + +# JSON +donnees = { + "nom": "Alice", + "age": 30, + "hobbies": ["lecture", "natation", "cuisine"] +} + +# Écriture JSON +with open("personne.json", "w") as f: + json.dump(donnees, f, indent=2, ensure_ascii=False) + +# Lecture JSON +with open("personne.json", "r") as f: + donnees_chargees = json.load(f) + +# CSV +donnees_csv = [ + ["nom", "age", "ville"], + ["Alice", 30, "Paris"], + ["Bob", 25, "Lyon"], + ["Charlie", 35, "Marseille"] +] + +# Écriture CSV +with open("personnes.csv", "w", newline='') as f: + writer = csv.writer(f) + writer.writerows(donnees_csv) + +# Lecture CSV avec DictReader +with open("personnes.csv", "r") as f: + reader = csv.DictReader(f) + for ligne in reader: + print(f"{ligne['nom']} a {ligne['age']} ans") +``` + +## Gestion des chemins et sys.path + +### Module sys +```python +import sys + +# Informations sur l'environnement Python +print(f"Version Python: {sys.version}") +print(f"Plateforme: {sys.platform}") +print(f"Chemin de l'exécutable: {sys.executable}") + +# Chemin de recherche des modules +print("Chemins de recherche des modules:") +for chemin in sys.path: + print(f" {chemin}") + +# Ajouter un chemin personnalisé +sys.path.insert(0, "/mon/chemin/personnel") + +# Arguments de la ligne de commande +print(f"Script lancé: {sys.argv[0]}") +print(f"Arguments: {sys.argv[1:]}") + +# Sortie du programme +# sys.exit(0) # Sortie normale +# sys.exit("Erreur critique") # Sortie avec message d'erreur +``` + +### Gestion dynamique des modules +```python +import importlib +import importlib.util +import sys +from pathlib import Path + +# Recharger un module modifié +import mon_module +# ... modifications du fichier mon_module.py ... +importlib.reload(mon_module) + +# Charger un module depuis un fichier spécifique +def charger_module_depuis_fichier(nom_module, chemin_fichier): + spec = importlib.util.spec_from_file_location(nom_module, chemin_fichier) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + +# Utilisation +chemin_module = Path("plugins/mon_plugin.py") +if chemin_module.exists(): + plugin = charger_module_depuis_fichier("mon_plugin", chemin_module) + +# Vérifier si un module peut être importé +def module_disponible(nom_module): + spec = importlib.util.find_spec(nom_module) + return spec is not None + +if module_disponible("numpy"): + import numpy as np +else: + print("NumPy n'est pas disponible") +``` + +## Modules et namespaces + +### Espaces de noms +```python +# Variables globales du module +variable_globale = "Je suis globale" + +def ma_fonction(): + variable_locale = "Je suis locale" + print(f"Locale: {variable_locale}") + print(f"Globale: {variable_globale}") + +# Introspection des espaces de noms +print("Variables globales du module:") +for nom, valeur in globals().items(): + if not nom.startswith('_'): + print(f" {nom}: {type(valeur)}") + +def explorer_locals(): + x = 10 + y = "local" + print("Variables locales:") + for nom, valeur in locals().items(): + print(f" {nom}: {valeur}") + +explorer_locals() +``` + +### Module privé et public +```python +# conventions.py + +# Variable "privée" (par convention) +_variable_privee = "Ne devrait pas être importée" + +# Variable publique +variable_publique = "Peut être importée" + +def _fonction_privee(): + """Fonction privée, ne devrait pas être utilisée à l'extérieur""" + return "privée" + +def fonction_publique(): + """Fonction publique, partie de l'API du module""" + return _fonction_privee() # Utilise la fonction privée en interne + +# Définir ce qui est exporté avec import * +__all__ = ['variable_publique', 'fonction_publique'] + +# Utilisation depuis un autre module +# from conventions import * # N'importe que les éléments dans __all__ +# from conventions import _variable_privee # Fonctionne mais pas recommandé +``` + +## Installation et gestion de modules externes + +### pip - Gestionnaire de packages +```bash +# Installation de base +pip install numpy +pip install pandas==1.3.0 # Version spécifique +pip install "django>=3.0,<4.0" # Plage de versions + +# Installation depuis requirements.txt +pip install -r requirements.txt + +# Mise à jour +pip upgrade numpy +pip upgrade --upgrade-strategy eager # Met à jour toutes les dépendances + +# Désinstallation +pip uninstall numpy + +# Informations +pip show numpy +pip list +pip list --outdated +``` + +### Fichier requirements.txt +```txt +# requirements.txt +# Packages de base pour la data science +numpy==1.21.0 +pandas==1.3.0 +matplotlib==3.4.2 +seaborn==0.11.1 +scikit-learn==0.24.2 + +# Packages de développement +pytest==6.2.4 +black==21.6b0 +flake8==3.9.2 + +# Packages optionnels +tensorflow==2.5.0 +jupyter==1.0.0 + +# Installation depuis git +git+https://github.com/utilisateur/repo.git@v1.0.0 +``` + +### Environnements virtuels +```bash +# Création d'un environnement virtuel +python -m venv mon_env + +# Activation (Linux/Mac) +source mon_env/bin/activate + +# Activation (Windows) +mon_env\Scripts\activate + +# Désactivation +deactivate + +# Installation dans l'environnement +pip install -r requirements.txt + +# Exporter l'environnement +pip freeze > requirements.txt +``` + +### Gestion avec conda +```bash +# Création d'environnement avec conda +conda create -n data_science python=3.9 +conda activate data_science + +# Installation de packages +conda install numpy pandas matplotlib +conda install -c conda-forge seaborn + +# Export de l'environnement +conda env export > environment.yml + +# Création depuis un fichier +conda env create -f environment.yml +``` + +## Bonnes pratiques + +### Structure de projet +``` +mon_projet/ +│ +├── README.md +├── requirements.txt +├── setup.py +├── .gitignore +│ +├── src/ # Code source +│ ├── __init__.py +│ ├── core/ +│ │ ├── __init__.py +│ │ ├── models.py +│ │ └── utils.py +│ └── data/ +│ ├── __init__.py +│ ├── loader.py +│ └── processor.py +│ +├── tests/ # Tests unitaires +│ ├── __init__.py +│ ├── test_models.py +│ └── test_utils.py +│ +├── docs/ # Documentation +│ └── README.md +│ +└── data/ # Données + ├── raw/ + ├── processed/ + └── external/ +``` + +### Imports propres +```python +# Ordre recommandé des imports (PEP 8) + +# 1. Bibliothèque standard +import os +import sys +from pathlib import Path +from typing import List, Dict, Optional + +# 2. Bibliothèques tierces +import numpy as np +import pandas as pd +import matplotlib.pyplot as plt + +# 3. Modules locaux +from .core import ProcesseurPrincipal +from .utils import nettoyer_donnees +from ..data.loader import ChargerCSV + +# Éviter les imports circulaires +# Ne pas faire: from module_a import fonction_b dans module_b +# et from module_b import fonction_a dans module_a +``` + +### Configuration et settings +```python +# settings.py +"""Configuration centralisée de l'application""" + +import os +from pathlib import Path +from typing import Dict, Any + +# Chemin de base +BASE_DIR = Path(__file__).parent.parent + +# Configuration par environnement +ENVIRONMENT = os.getenv('ENVIRONMENT', 'development') + +def get_config() -> Dict[str, Any]: + """Retourne la configuration selon l'environnement""" + + # Configuration commune + config = { + 'BASE_DIR': BASE_DIR, + 'DATA_DIR': BASE_DIR / 'data', + 'LOGS_DIR': BASE_DIR / 'logs', + 'VERSION': '1.0.0', + } + + # Configuration spécifique à l'environnement + if ENVIRONMENT == 'development': + config.update({ + 'DEBUG': True, + 'LOG_LEVEL': 'DEBUG', + 'DATABASE_URL': 'sqlite:///dev.db', + }) + + elif ENVIRONMENT == 'production': + config.update({ + 'DEBUG': False, + 'LOG_LEVEL': 'INFO', + 'DATABASE_URL': os.getenv('DATABASE_URL'), + }) + + return config + +# Singleton de configuration +CONFIG = get_config() + +# Utilisation dans d'autres modules +# from settings import CONFIG +# print(CONFIG['VERSION']) +``` + +### Logging et documentation +```python +# logger_config.py +"""Configuration du système de logging""" + +import logging +import sys +from pathlib import Path + +def configurer_logger(nom: str, niveau: str = 'INFO', + fichier: Path = None) -> logging.Logger: + """ + Configure et retourne un logger. + + Args: + nom: Nom du logger + niveau: Niveau de logging ('DEBUG', 'INFO', 'WARNING', 'ERROR') + fichier: Fichier de log optionnel + + Returns: + Logger configuré + """ + + logger = logging.getLogger(nom) + logger.setLevel(getattr(logging, niveau.upper())) + + # Format des messages + formatter = logging.Formatter( + '%(asctime)s - %(name)s - %(levelname)s - %(message)s' + ) + + # Handler pour la console + console_handler = logging.StreamHandler(sys.stdout) + console_handler.setFormatter(formatter) + logger.addHandler(console_handler) + + # Handler pour fichier si spécifié + if fichier: + fichier.parent.mkdir(exist_ok=True) + file_handler = logging.FileHandler(fichier) + file_handler.setFormatter(formatter) + logger.addHandler(file_handler) + + return logger + +# Utilisation dans un module +# logger = configurer_logger(__name__, 'DEBUG', Path('logs/app.log')) +# logger.info("Application démarrée") +``` + +### Tests de modules +```python +# tests/test_utils.py +"""Tests pour le module utils""" + +import pytest +import sys +from pathlib import Path + +# Ajouter le chemin source pour les imports +sys.path.insert(0, str(Path(__file__).parent.parent / 'src')) + +from core.utils import calculer_moyenne, nettoyer_donnees + +class TestCalculerMoyenne: + """Tests pour la fonction calculer_moyenne""" + + def test_moyenne_nombres_positifs(self): + """Test avec des nombres positifs""" + resultat = calculer_moyenne([1, 2, 3, 4, 5]) + assert resultat == 3.0 + + def test_moyenne_avec_zero(self): + """Test avec zéro inclus""" + resultat = calculer_moyenne([0, 2, 4]) + assert resultat == 2.0 + + def test_liste_vide_leve_exception(self): + """Test qu'une liste vide lève une exception""" + with pytest.raises(ValueError, match="La liste ne peut pas être vide"): + calculer_moyenne([]) + + def test_moyenne_un_element(self): + """Test avec un seul élément""" + resultat = calculer_moyenne([42]) + assert resultat == 42.0 + +class TestNettoyerDonnees: + """Tests pour la fonction nettoyer_donnees""" + + def test_suppression_valeurs_nulles(self): + """Test suppression des valeurs nulles""" + donnees = [1, None, 3, None, 5] + resultat = nettoyer_donnees(donnees) + assert resultat == [1, 3, 5] + + @pytest.fixture + def donnees_test(self): + """Fixture avec des données de test""" + return [1, 2, None, 4, None, 6] + + def test_avec_fixture(self, donnees_test): + """Test utilisant une fixture""" + resultat = nettoyer_donnees(donnees_test) + assert len(resultat) == 4 + assert None not in resultat + +# Lancement des tests : pytest tests/test_utils.py -v +``` + +## Modules avancés pour la Data Science + +### numpy - Calcul numérique +```python +import numpy as np + +# Création d'arrays +arr = np.array([1, 2, 3, 4, 5]) +matrice = np.array([[1, 2], [3, 4]]) + +# Opérations vectorisées +resultat = arr * 2 # Multiplication par scalaire +somme = np.sum(arr) +moyenne = np.mean(arr) + +# Fonctions mathématiques +angles = np.linspace(0, 2*np.pi, 100) +sinus = np.sin(angles) +cosinus = np.cos(angles) + +# Algèbre linéaire +a = np.array([[1, 2], [3, 4]]) +b = np.array([[5, 6], [7, 8]]) +produit = np.dot(a, b) # Produit matriciel +``` + +### pandas - Manipulation de données +```python +import pandas as pd + +# Création de DataFrame +donnees = { + 'nom': ['Alice', 'Bob', 'Charlie'], + 'age': [25, 30, 35], + 'salaire': [50000, 60000, 70000] +} +df = pd.DataFrame(donnees) + +# Opérations de base +print(df.head()) +print(df.describe()) +print(df.info()) + +# Filtrage et sélection +jeunes = df[df['age'] < 30] +noms_ages = df[['nom', 'age']] + +# Lecture/écriture de fichiers +df.to_csv('employes.csv', index=False) +df_charge = pd.read_csv('employes.csv') + +# Groupement et agrégation +groupe_age = df.groupby('age')['salaire'].mean() +``` + +### matplotlib - Visualisation +```python +import matplotlib.pyplot as plt + +# Graphique simple +x = [1, 2, 3, 4, 5] +y = [2, 4, 6, 8, 10] + +plt.figure(figsize=(8, 6)) +plt.plot(x, y, marker='o') +plt.title('Mon Graphique') +plt.xlabel('X') +plt.ylabel('Y') +plt.grid(True) +plt.show() + +# Sous-graphiques +fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4)) + +ax1.plot(x, y) +ax1.set_title('Graphique 1') + +ax2.scatter(x, y) +ax2.set_title('Graphique 2') + +plt.tight_layout() +plt.show() +``` + +### requests - Requêtes HTTP +```python +import requests +import json + +# Requête GET simple +response = requests.get('https://api.github.com/users/octocat') + +if response.status_code == 200: + data = response.json() + print(f"Utilisateur: {data['login']}") + print(f"Nom: {data['name']}") + +# Requête POST avec données +donnees = {'nom': 'Alice', 'email': 'alice@example.com'} +headers = {'Content-Type': 'application/json'} + +response = requests.post( + 'https://httpbin.org/post', + json=donnees, + headers=headers +) + +# Gestion des erreurs +try: + response = requests.get('https://api.inexistante.com', timeout=5) + response.raise_for_status() # Lève une exception si erreur HTTP +except requests.exceptions.RequestException as e: + print(f"Erreur de requête: {e}") +``` + +### Exemple d'intégration - Module de data science +```python +# data_analyzer.py +""" +Module d'analyse de données complet +""" + +import pandas as pd +import numpy as np +import matplotlib.pyplot as plt +import seaborn as sns +from pathlib import Path +from typing import Optional, Dict, List +import logging + +# Configuration du logger +logger = logging.getLogger(__name__) + +class AnalyseurDonnees: + """Classe principale pour l'analyse de données""" + + def __init__(self, donnees: Optional[pd.DataFrame] = None): + """ + Initialise l'analyseur. + + Args: + donnees: DataFrame pandas optionnel + """ + self.donnees = donnees + self.resultats = {} + + if donnees is not None: + logger.info(f"Analyseur initialisé avec {len(donnees)} lignes") + + def charger_csv(self, chemin: Path) -> pd.DataFrame: + """Charge des données depuis un fichier CSV""" + try: + self.donnees = pd.read_csv(chemin) + logger.info(f"Données chargées depuis {chemin}") + return self.donnees + except Exception as e: + logger.error(f"Erreur lors du chargement: {e}") + raise + + def nettoyer_donnees(self) -> pd.DataFrame: + """Nettoie les données (supprime les valeurs manquantes)""" + if self.donnees is None: + raise ValueError("Aucune donnée à nettoyer") + + avant = len(self.donnees) + self.donnees = self.donnees.dropna() + apres = len(self.donnees) + + logger.info(f"Nettoyage: {avant - apres} lignes supprimées") + return self.donnees + + def analyser_statistiques(self) -> Dict: + """Calcule les statistiques descriptives""" + if self.donnees is None: + raise ValueError("Aucune donnée à analyser") + + self.resultats['stats'] = { + 'forme': self.donnees.shape, + 'description': self.donnees.describe(), + 'types': self.donnees.dtypes, + 'valeurs_manquantes': self.donnees.isnull().sum() + } + + logger.info("Statistiques calculées") + return self.resultats['stats'] + + def generer_graphiques(self, dossier_sortie: Path = Path('plots')): + """Génère des graphiques d'analyse""" + if self.donnees is None: + raise ValueError("Aucune donnée pour les graphiques") + + dossier_sortie.mkdir(exist_ok=True) + + # Matrice de corrélation + plt.figure(figsize=(10, 8)) + sns.heatmap(self.donnees.corr(), annot=True, cmap='coolwarm') + plt.title('Matrice de Corrélation') + plt.savefig(dossier_sortie / 'correlation.png', dpi=300, bbox_inches='tight') + plt.close() + + # Distribution des variables numériques + colonnes_num = self.donnees.select_dtypes(include=[np.number]).columns + + for col in colonnes_num: + plt.figure(figsize=(8, 6)) + plt.hist(self.donnees[col], bins=30, alpha=0.7) + plt.title(f'Distribution de {col}') + plt.xlabel(col) + plt.ylabel('Fréquence') + plt.savefig(dossier_sortie / f'dist_{col}.png', dpi=300, bbox_inches='tight') + plt.close() + + logger.info(f"Graphiques sauvegardés dans {dossier_sortie}") + + def exporter_rapport(self, chemin: Path = Path('rapport.html')): + """Exporte un rapport HTML""" + if not self.resultats: + self.analyser_statistiques() + + html = f""" + +
Forme des données: {self.resultats['stats']['forme']}
+