En Python, les itérateurs, les générateurs et les décorateurs sont des concepts avancés centraux pour la manipulation des séquences de données et l'amélioration des fonctions. Ils permettent de parcourir efficacement des collections, de gérer la mémoire de manière optimale, de créer des flux de données personnalisés et d'étendre les fonctionnalités des fonctions existantes. Explorons leur fonctionnement et leurs cas d'utilisation.
1. Itérateurs (Iterators)
1.1 Qu'est-ce qu'un Itérateur ?
Un itérateur est un objet qui permet de parcourir un conteneur (comme une liste, un tuple ou un dictionnaire) élément par élément. Il implémente deux méthodes principales :
- __iter__() : Renvoie l'objet itérateur lui-même.
- __next__() : Renvoie l'élément suivant dans le conteneur. Si aucun élément n'est disponible, il lève une exception StopIteration.
1.2 Exemple Basique
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
class Compteur: def __init__(self, limite): self.limite = limite self.valeur = 0 def __iter__(self): return self def __next__(self): if self.valeur < self.limite: self.valeur += 1 return self.valeur else: raise StopIteration # Utilisation for nombre in Compteur(5): print(nombre) # Affiche 1, 2, 3, 4, 5 |
1.3 Itérateurs Intégrés
Les conteneurs standards (listes, ensembles, etc.) disposent d'itérateurs intégrés :
|
1 2 3 |
ma_liste = [1, 2, 3] iterateur = iter(ma_liste) # Équivalent à ma_liste.__iter__() print(next(iterateur)) # 1 (équivalent à iterateur.__next__()) |
2. Générateurs (Generators)
2.1 Qu'est-ce qu'un Générateur ?
Un générateur est un type d'itérateur spécial qui génère des valeurs "à la volée" au lieu de les stocker en mémoire. Il utilise le mot-clé yield pour produire des valeurs une par une.
2.2 Exemple avec une Fonction Générateur
|
1 2 3 4 5 6 7 8 9 |
def genere_puissances_de_deux(limite): compteur = 0 while compteur < limite: yield 2 ** compteur compteur += 1 # Utilisation for puissance in genere_puissances_de_deux(5): print(puissance) # Affiche 1, 2, 4, 8, 16 |
2.3 Avantages des Générateurs
- Économie de mémoire : Les valeurs sont générées uniquement lorsqu'elles sont demandées.
- Évaluation paresseuse : Idéal pour traiter des flux de données infinis ou de grande taille.
2.4 Expressions Générateur
Similaires aux compréhensions de listes, mais avec des parenthèses :
|
1 2 3 |
gen_carres = (x**2 for x in range(5)) for carre in gen_carres: print(carre) # Affiche 0, 1, 4, 9, 16 |
3. Décorateurs (Decorators)
3.1 Qu'est-ce qu'un Décorateur ?
Un décorateur est une fonction qui modifie le comportement d'une autre fonction sans en changer le code source. C'est un outil puissant pour ajouter des fonctionnalités à des fonctions existantes de manière propre et réutilisable.
3.2 Exemple Basique de Décorateur
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
def mon_decorateur(func): def fonction_interne(): print("Avant l'appel de la fonction") func() print("Après l'appel de la fonction") return fonction_interne @mon_decorateur def dire_bonjour(): print("Bonjour !") # Utilisation dire_bonjour() # Affiche: # Avant l'appel de la fonction # Bonjour ! # Après l'appel de la fonction |
3.3 Décorateurs avec Paramètres
Les décorateurs peuvent également accepter des paramètres :
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
def repetitif(nombre_repetitions): def decorateur_repetitif(func): def fonction_interne(*args, **kwargs): for i in range(nombre_repetitions): func(*args, **kwargs) return fonction_interne return decorateur_repetitif @repetitif(3) def saluer(nom): print(f"Bonjour {nom}!") saluer("Alice") # Affiche: # Bonjour Alice! # Bonjour Alice! # Bonjour Alice! |
3.4 Décorateurs appliqués aux classes
Les décorateurs peuvent également être appliqués à des classes :
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
def ajouter_methodes(cls): def nouvelle_methode(self): return f"Méthode ajoutée à {self.__class__.__name__}" cls.methode_ajoutee = nouvelle_methode return cls @ajouter_methodes class MaClasse: def __init__(self, valeur): self.valeur = valeur obj = MaClasse(42) print(obj.methode_ajoutee()) # Affiche: Méthode ajoutée à MaClasse |
3.5 Utilisations Courantes des Décorateurs
- Journalisation (Logging) : Enregistrer les appels de fonction
- Contrôle d'accès : Vérifier les permissions avant d'exécuter une fonction
- Mise en cache : Stocker les résultats de fonctions coûteuses
- Validation : Vérifier les types ou valeurs des paramètres
- Mesure de performance : Chronométrer le temps d'exécution
4. Différences Clés
| Itérateur | Générateur | Décorateur |
|---|---|---|
| Implémente __iter__ et __next__. | Utilise yield pour produire des valeurs. | Modifie le comportement d'une fonction/classe. |
| Peut être réutilisé. | Non réutilisable après consommation. | Réutilisable sur plusieurs fonctions. |
| Stocke l'état dans des attributs. | Maintient l'état local automatiquement. | Agit comme un wrapper autour de la fonction. |
5. Cas d'Usage Courants
5.1 Parcourir de Grands Fichiers
|
1 2 3 4 5 6 |
def lire_lignes(fichier): with open(fichier, 'r') as f: for ligne in f: yield ligne.strip() # Lit un fichier ligne par ligne sans charger l'intégralité en mémoire. |
5.2 Séquences Infinies
|
1 2 3 4 5 6 7 8 9 |
def fibonacci_infini(): a, b = 0, 1 while True: yield a a, b = b, a + b fib = fibonacci_infini() for _ in range(10): print(next(fib)) # Affiche les 10 premiers termes de Fibonacci. |
5.3 Décorateur de Mesure de Performance
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
import time def chronometre(func): def fonction_interne(*args, **kwargs): debut = time.time() resultat = func(*args, **kwargs) fin = time.time() print(f"Temps d'exécution de {func.__name__}: {fin - debut:.4f} secondes") return resultat return fonction_interne @chronometre def fonction_coûteuse(): time.sleep(1) return "Terminé" fonction_coûteuse() # Affiche le temps d'exécution |
5.4 Meilleures Pratiques
- Utilisez des générateurs pour des données volumineuses ou infinies.
- Privilégiez les expressions générateur aux listes pour des traitements en pipeline.
- Combinez les générateurs avec des fonctions comme itertools pour des opérations complexes.
- Utilisez les décorateurs pour séparer les préoccupations transversales (logging, caching, etc.).
- Pensez à utiliser functools.wraps dans vos décorateurs pour préserver les métadonnées des fonctions.
6. Conclusion
Les itérateurs, les générateurs et les décorateurs sont des outils puissants pour optimiser les performances, la lisibilité et la maintenabilité du code Python. Maîtriser leur utilisation permet de créer des applications plus efficaces, évolutives et faciles à maintenir.
Younes Derfoufi



