5.1 Iterable
et Iterator
Comme tu le sais déjà, les itérateurs sont des objets qui implémentent le protocole de l'itérateur, permettant de récupérer séquentiellement les éléments d'une collection. Les itérateurs sont largement utilisés en Python pour parcourir les éléments de séquences comme les listes, tuples et chaînes.
Voyons comment fonctionnent les itérateurs et comment les utiliser.
Objet itérable (Iterable)
Pour qu'un objet puisse être parcouru avec une boucle for
, il doit
être itérable – un Iterable
. Cela signifie que notre objet doit
implémenter la méthode __iter__()
,
qui retourne un objet itérateur.
Objet itérateur (Iterator)
C'est un objet spécial qui possède
la fonction __next__()
pour donner
l'élément suivant de la séquence. Quand tous les éléments
sont épuisés, la méthode __next__()
déclenche une exception
StopIteration
comme signal d'arrêt d'itération.
L'itérateur doit aussi implémenter la méthode __iter__()
,
qui renvoie l'itérateur lui-même.
Exemple avec les fonctions intégrées de Python
Dans cet exemple, la liste
numbers est un objet itérable. Nous obtenons un itérateur grâce à la fonction
iter()
et utilisons la fonction
next()
pour parcourir les éléments jusqu'à ce que
l'exception StopIteration
soit levée.
# Objet itérable
numbers = [1, 2, 3, 4, 5]
# Obtenir un itérateur à partir de l'itérable
iterator = iter(numbers)
# Utiliser l'itérateur pour parcourir les éléments
try:
while True:
number = next(iterator)
print(number)
except StopIteration:
pass
C'est exactement ce qui se passe quand tu écris du code comme :
# Objet itérable
numbers = [1, 2, 3, 4, 5]
for number in numbers:
print(number)
5.2 L'essence d'un itérateur
Un itérateur est un certain objet qui nous aide à parcourir séquentiellement un groupe d'éléments. Les implémentations peuvent être très variées. Écrivons notre propre classe, où nous implémenterons toutes les exigences requises pour un itérateur.
Étape 1. Créons d'abord notre classe
Disons qu'elle retourne séquentiellement les nombres de start
à
end
.
class MyIterator:
def __init__(self, start, end):
self.current = start
self.end = end
Étape 2. Support de la fonction __iter__
Maintenant, nous devons lui ajouter une fonction __iter__
,
qui renverra un objet itérateur sur lequel sera appelée
la fonction __next()__
. Nous retournerons
une référence sur notre propre objet – ce qui est autorisé.
class MyIterator:
def __init__(self, start, end):
self.current = start
self.end = end
def __iter__(self):
return self
Étape 3. Support de la fonction __next__
Nous devons maintenant ajouter à notre objet itérateur la fonction __next__
, qui renverra
le prochain élément de notre liste. Nous utiliserons simplement la variable current
:
def __next__(self):
current = self.current
self.current += 1
return current
Étape 4. Arrêt de l'itérateur
Si l'itérateur a déjà retourné toutes les valeurs qu'il prévoyait, il doit lancer une exception StopIteration
. Ajustons
un peu notre dernière fonction :
def __next__(self):
if self.current >= self.end:
raise StopIteration
current = self.current
self.current += 1
return current
Super. Maintenant, nous pouvons utiliser notre itérateur. Voici un exemple de tout notre code :
class MyIterator:
def __init__(self, start, end):
self.current = start
self.end = end
def __iter__(self):
return self
def __next__(self):
if self.current >= self.end:
raise StopIteration
current = self.current
self.current += 1
return current
# Créer une instance de l'itérateur personnalisé
my_iter = MyIterator(1, 5)
# Utiliser l'itérateur pour parcourir les éléments
for num in my_iter:
print(num)
5.3 Un bon itérateur
Pourquoi l'itérateur de l'exemple précédent est-il mauvais ? Oui, c'est un itérateur, il fonctionne, mais il est trop basique. Avec lui, impossible de parcourir la même collection d'éléments avec plusieurs itérateurs en même temps.
Il serait plus judicieux d'écrire un code qui ne retourne pas une référence à lui-même dans la méthode __iter__
, mais
un objet séparé, qui délivrerait correctement tous les éléments.
Exemple :
class MyIterable:
def __init__(self, data):
self.data = data
def __iter__(self):
return MyIterator(self.data)
class MyIterator:
def __init__(self, data):
self.data = data
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index >= len(self.data):
raise StopIteration
item = self.data[self.index]
self.index += 1
return item
# Utilisation
my_iterable = MyIterable([1, 2, 3, 4])
for item in my_iterable:
print(item)
Dans cet exemple, nous avons deux classes — la première reçoit une collection sur laquelle nous allons itérer. Et la seconde —
c'est l'itérateur qui retourne les éléments de la collection dans la méthode next()
. Il est aussi assez simple,
mais c'est ainsi qu'il faut ajouter les itérateurs dans vos classes.
GO TO FULL VERSION