Patron d'itérateur Python
Imaginez que vous avez un bocal rempli de bonbons de différentes couleurs. Vous voulez goûter ces bonbons un par un, mais vous ne voulez pas verser tous les bonbons d'un seul coup. À ce moment-là, vous plongez la main dans le bocal et prenez un bonbon à la fois—ce processus est l'itération.
En programmation,le patron de conception Itérateurest un patron de conception qui fournit un moyen d'accéder séquentiellement aux éléments d'un objet collection sans exposer sa représentation interne.
Concepts fondamentaux
Le patron de conception Itérateur se compose de deux composants principaux :
- Itérateur: Responsable de définir l'interface pour accéder aux éléments et les parcourir
- Itérable: Fournit une méthode pour créer des itérateurs
En Python, le patron de conception Itérateur a été profondément intégré aux fonctionnalités de base du langage, rendant notre programmation plus élégante et intuitive.
Pourquoi avons-nous besoin d'itérateurs ?
Limites de l'approche traditionnelle
Regardons d'abord un exemple qui n'utilise pas d'itérateurs :
Exemple
class BookCollection:
def __init__(self):
self.books = ["Python Introduction", "Introduction to Algorithms", "Design Patterns", "Data Structures"]
def get_books(self):
return self.books
# Utiliser cette collection
collection = BookCollection()
books = collection.get_books()
# Parcourir books - expose les détails d'implémentation internes
for i in range(len(books)):
print(books[i])
Analyse du problème :
- Le code client doit connaître la structure interne de la collection (une liste)
- Si l'implémentation interne de la collection change (par exemple, d'une liste à un dictionnaire), tout le code client doit être modifié
- La logique de parcours est étroitement couplée à l'implémentation de la collection
Avantages des itérateurs
Exemple
for book in collection:
print(book)
Avantages de l'utilisation des itérateurs :
- Encapsulation: Masque l'implémentation interne de la collection
- Interface unifiée: Différents types de collections peuvent utiliser la même méthode de parcours
- Flexibilité: Vous pouvez facilement changer l'implémentation d'une collection
- Parcours multiples pris en charge: Plusieurs opérations de parcours peuvent être effectuées simultanément
Le protocole des itérateurs en Python
Python implémente le protocole des itérateurs via deux méthodes spéciales :
__iter__()Méthode
- Renvoie un objet itérateur
- Un objet itérable doit implémenter cette méthode
__next__()Méthode
- Renvoie l'élément suivant de la séquence
- Lorsqu'il n'y a plus d'éléments, lever
StopIterationune exception
Comprenons comment fonctionnent les itérateurs à l'aide d'un organigramme :

Création d'itérateurs personnalisés
Méthode 1 : Implémenter un itérateur avec une classe
Créons un itérateur de livre personnalisé :
Exemple
"""Classe d'itérateur de livre"""
def __init__(self, books):
self.books = books
self.index = 0
def __iter__(self):
"""Renvoie l'itérateur lui-même"""
return self
def __next__(self):
"""Renvoie le livre suivant, ou lève StopIteration s'il n'y a plus de livres"""
if self.index < len(self.books):
book = self.books[self.index]
self.index += 1
return book
else:
raise StopIteration
class BookCollection:
"""Classe de collection de livres itérable"""
def __init__(self):
self.books = ["Python Introduction", "Introduction to Algorithms", "Design Patterns", "Data Structures"]
def __iter__(self):
"""Renvoie une instance d'itérateur"""
return BookIterator(self.books)
# Utiliser l'itérateur personnalisé
collection = BookCollection()
for book in collection:
print(f"Lecture : {book}")
Sortie :
正在阅读: Python入门 正在阅读: 算法导论 正在阅读: 设计模式 正在阅读: 数据结构
Méthode 2 : Utilisation de fonctions génératrices
Python offre un moyen plus concis — les fonctions génératrices :
Exemple
def __init__(self):
self.books = ["Python Introduction", "Introduction to Algorithms", "Design Patterns", "Data Structures"]
def __iter__(self):
"""Créer un itérateur à l'aide d'une fonction génératrice"""
for book in self.books:
yield book
# L'utilisation est exactement la même
collection = BookCollection()
for book in collection:
print(f"Lecture : {book}")
Avantages des générateurs :
- Code plus propre
- Gère automatiquement la sauvegarde de l'état
- Meilleures performances
Cas d'utilisation pratiques pour les itérateurs
Scénario 1 : Lecture de données paginées
Exemple
"""Simuler un itérateur de données paginées"""
def __init__(self, total_items, page_size=3):
self.total_items = total_items
self.page_size = page_size
self.current_page = 0
def __iter__(self):
return self
def __next__(self):
start = self.current_page * self.page_size
end = start + self.page_size
if start >= self.total_items:
raise StopIteration
# Simuler la lecture d'une page de données depuis la base de données
page_data = list(range(start, min(end, self.total_items)))
self.current_page += 1
return page_data
# Utiliser l'itérateur paginé
paginator = PaginatedData(10, 3) # 10 enregistrements au total, 3 par page
for page_num, page_data in enumerate(paginator, 1):
print(f"Données de la page {page_num} : {page_data}")
Sortie :
第1页数据: [0, 1, 2] 第2页数据: [3, 4, 5] 第3页数据: [6, 7, 8] 第4页数据: [9]
Scénario 2 : Génération de séquences infinies
Exemple
"""Itérateur de la suite de Fibonacci"""
def __init__(self, max_count=10):
self.max_count = max_count
self.count = 0
self.a, self.b = 0, 1
def __iter__(self):
return self
def __next__(self):
if self.count >= self.max_count:
raise StopIteration
result = self.a
self.a, self.b = self.b, self.a + self.b
self.count += 1
return result
# Générer la suite de Fibonacci
fib = FibonacciIterator(8)
print("Suite de Fibonacci :", list(fib))
Sortie :
斐波那契数列: [0, 1, 1, 2, 3, 5, 8, 13]
Utilitaires d'itérateurs intégrés
Python fournit un riche ensemble de fonctions intégrées pour travailler avec les itérateurs :
iter()etnext()Fonction
Exemple
# Utiliser l'itérateur manuellement
iterator = iter(numbers)
print(next(iterator)) # Sortie : 1
print(next(iterator)) # Sortie : 2
print(next(iterator)) # Sortie : 3
enumerate()Fonction
Exemple
for index, fruit in enumerate(fruits):
print(f"Indice {index}: {fruit}")
zip()Fonction
Exemple
scores = [85, 92, 78]
for name, score in zip(names, scores):
print(f"{name} score: {score}")
Itérateur vs Itérable
Il est important de comprendre la différence entre ces deux concepts :
| Fonctionnalité | Itérable | Iterator |
|---|---|---|
| Définition | Implémente__iter__()l'objet de la méthode |
Implémente__iter__()et__next__()l'objet de la méthode |
| Objectif | Peut être itéré | Effectue réellement l'opération d'itération |
| State | Généralement sans état | Maintient l’état d’itération (position actuelle, etc.) |
| Exemple | Listes, tuples, dictionnaires, chaînes | iter()L’objet retourné |
Diagramme de relations
Exemple
A[Iterable] -->|appelle iter| B[Iterator]
B -->|appelle next de façon répétée| C[Élément par élément]
C --> D[Jusqu’à StopIteration]
Bonnes pratiques et erreurs courantes
Bonnes pratiques
- Utilisez des générateurs pour simplifier le code
Exemple
def countdown(n):
while n > 0:
yield n
n -= 1
# Non recommandé : implémenter manuellement une classe d'itérateur
- Tirer parti des fonctions intégrées
Exemple
squares = (x*x for x in range(10)) # Expression génératrice
# Non recommandé
class SquareIterator:
# ... implémentation verbeuse
Erreurs courantes
Erreur 1 : confondre les itérateurs et les itérables
Exemple
# Erreur : la liste elle-même n'est pas un itérateur
try:
next(numbers) # TypeError : l'objet 'list' n'est pas un itérateur
except TypeError as e:
print(f"Erreur: {e}")
# Correct : obtenir d'abord l'itérateur
iterator = iter(numbers)
print(next(iterator)) # Sortie : 1
Erreur 2 : Continuer à utiliser un itérateur épuisé
Exemple
iterator = iter(numbers)
print(list(iterator)) # Sortie : [1, 2, 3]
print(list(iterator)) # Sortie : [] - itérateur épuisé !
Exercices pratiques
Exercice 1 : Créer un itérateur personnalisé
Créer unCountdownclasse qui implémente un itérateur comptant à rebours depuis un nombre spécifié jusqu'à 1 :
Exemple
def __init__(self, start):
self.start = start
def __iter__(self):
# Votre code ici
current = self.start
while current > 0:
yield current
current -= 1
# Testez votre implémentation
for num in Countdown(5):
print(num) # Sortie attendue : 5, 4, 3, 2, 1
Exercice 2 : Itérateur de lignes de fichier
Créez un itérateur qui lit un fichier ligne par ligne et ajoute un numéro de ligne avant chaque ligne :
Exemple
def __init__(self, filename):
self.filename = filename
def __iter__(self):
with open(self.filename, 'r', encoding='utf-8') as file:
for line_num, line in enumerate(file, 1):
yield f"{line_num}: {line.rstrip()}"
Résumé
Le modèle d'itérateur est un concept extrêmement important en programmation Python ; il nous permet de :
- Accès uniforme: parcourir différentes structures de données de la même manière
- Encapsulation de l'implémentation: masquer la structure interne des collections
- Évaluation paresseuse: générer des données uniquement lorsque c'est nécessaire, ce qui permet d'économiser de la mémoire
- Composabilité: fonctionne parfaitement avec des fonctionnalités telles que les générateurs et les compréhensions
Rappelez-vous ce principe simple :Tout objet qui implémente__iter__()la méthode est itérable, et tout objet qui implémente__next__()la méthode est un itérateur.