Itérateurs Python (__iter__ et __next__): comment l'utiliser et pourquoi?

Les itérateurs sont des objets sur lesquels il est possible d'itérer. Dans ce didacticiel, vous apprendrez comment fonctionne l'itérateur et comment vous pouvez créer votre propre itérateur à l'aide des méthodes __iter__ et __next__.

Vidéo: itérateurs Python

Itérateurs en Python

Les itérateurs sont partout en Python. Ils sont élégamment implémentés dans des forboucles, des compréhensions, des générateurs, etc. mais sont cachés à la vue de tous.

Iterator en Python est simplement un objet sur lequel il peut être itéré. Un objet qui renverra des données, un élément à la fois.

Techniquement parlant, un objet itérateur Python doit implémenter deux méthodes spéciales __iter__()et __next__(), collectivement, appelé protocole itérateur .

Un objet est appelé itérable si nous pouvons en obtenir un itérateur. La plupart des conteneurs intégrés en Python tels que: list, tuple, string, etc. sont des itérables.

La iter()fonction (qui à son tour appelle la __iter__()méthode) en renvoie un itérateur.

Itérer via un itérateur

Nous utilisons la next()fonction pour parcourir manuellement tous les éléments d'un itérateur. Lorsque nous atteignons la fin et qu'il n'y a plus de données à renvoyer, cela déclenchera l' StopIterationexception. Voici un exemple.

 # define a list my_list = (4, 7, 0, 3) # get an iterator using iter() my_iter = iter(my_list) # iterate through it using next() # Output: 4 print(next(my_iter)) # Output: 7 print(next(my_iter)) # next(obj) is same as obj.__next__() # Output: 0 print(my_iter.__next__()) # Output: 3 print(my_iter.__next__()) # This will raise error, no items left next(my_iter)

Production

 4 7 0 3 Traceback (dernier appel le plus récent): Fichier "", ligne 24, dans le prochain (my_iter) StopIteration

Une manière plus élégante d'itérer automatiquement consiste à utiliser la boucle for. En utilisant cela, nous pouvons itérer sur n'importe quel objet pouvant renvoyer un itérateur, par exemple une liste, une chaîne, un fichier, etc.

 >>> for element in my_list:… print(element)… 4 7 0 3

Fonctionnement de la boucle for pour les itérateurs

Comme nous le voyons dans l'exemple ci-dessus, la forboucle a pu itérer automatiquement dans la liste.

En fait, la forboucle peut itérer sur n'importe quel itérable. Examinons de plus près comment la forboucle est réellement implémentée en Python.

 for element in iterable: # do something with element

Est en fait implémenté comme.

 # create an iterator object from that iterable iter_obj = iter(iterable) # infinite loop while True: try: # get the next item element = next(iter_obj) # do something with element except StopIteration: # if StopIteration is raised, break from loop break

Donc en interne, la forboucle crée un objet itérateur, iter_objen appelant iter()l'itérable.

Ironiquement, cette forboucle est en fait une boucle while infinie.

À l'intérieur de la boucle, il appelle next()pour obtenir l'élément suivant et exécute le corps de la forboucle avec cette valeur. Une fois tous les éléments épuisés, il StopIterationest soulevé qui est capturé en interne et la boucle se termine. Notez que tout autre type d'exception passera.

Création d'itérateurs personnalisés

Construire un itérateur à partir de zéro est facile en Python. Nous devons juste implémenter __iter__()les __next__()méthodes et.

La __iter__()méthode renvoie l'objet itérateur lui-même. Si nécessaire, une initialisation peut être effectuée.

La __next__()méthode doit renvoyer l'élément suivant dans la séquence. À la fin, et dans les appels suivants, il doit augmenter StopIteration.

Ici, nous montrons un exemple qui nous donnera la prochaine puissance de 2 à chaque itération. L'exposant de puissance commence de zéro jusqu'à un nombre défini par l'utilisateur.

Si vous n'avez aucune idée de la programmation orientée objet, visitez la page Programmation orientée objet Python.

 class PowTwo: """Class to implement an iterator of powers of two""" def __init__(self, max=0): self.max = max def __iter__(self): self.n = 0 return self def __next__(self): if self.n <= self.max: result = 2 ** self.n self.n += 1 return result else: raise StopIteration # create an object numbers = PowTwo(3) # create an iterable from the object i = iter(numbers) # Using next to get to the next iterator element print(next(i)) print(next(i)) print(next(i)) print(next(i)) print(next(i))

Production

 1 2 4 8 Traceback (dernier appel le plus récent): Fichier "/home/bsoyuj/Desktop/Untitled-1.py", ligne 32, en version imprimée (suivant (i)) Fichier "", ligne 18, dans __next__ lever StopIteration StopIteration

Nous pouvons également utiliser une forboucle pour parcourir notre classe d'itérateur.

 >>> for i in PowTwo(5):… print(i)… 1 2 4 8 16 32

Itérateurs infinis Python

Il n'est pas nécessaire que l'élément dans un objet itérateur soit épuisé. Il peut y avoir des itérateurs infinis (qui ne finissent jamais). Nous devons être prudents lors de la manipulation de tels itérateurs.

Voici un exemple simple pour démontrer des itérateurs infinis.

La fonction de fonction iter()intégrée peut être appelée avec deux arguments où le premier argument doit être un objet appelable (fonction) et le second est la sentinelle. L'itérateur appelle cette fonction jusqu'à ce que la valeur renvoyée soit égale à la sentinelle.

 >>> int() 0 >>> inf = iter(int,1) >>> next(inf) 0 >>> next(inf) 0

Nous pouvons voir que la int()fonction retourne toujours 0. Donc, la passer comme iter(int,1)retournera un itérateur qui appelle int()jusqu'à ce que la valeur retournée soit égale à 1. Cela ne se produit jamais et nous obtenons un itérateur infini.

Nous pouvons également construire nos propres itérateurs infinis. L'itérateur suivant renverra, en théorie, tous les nombres impairs.

 class InfIter: """Infinite iterator to return all odd numbers""" def __iter__(self): self.num = 1 return self def __next__(self): num = self.num self.num += 2 return num

Un exemple d’exécution serait le suivant.

 >>> a = iter(InfIter()) >>> next(a) 1 >>> next(a) 3 >>> next(a) 5 >>> next(a) 7

Etc…

Veillez à inclure une condition de fin lors de l'itération sur ces types d'itérateurs infinis.

L'avantage d'utiliser des itérateurs est qu'ils économisent des ressources. Comme indiqué ci-dessus, nous pourrions obtenir tous les nombres impairs sans stocker le système numérique entier en mémoire. Nous pouvons avoir des éléments infinis (théoriquement) en mémoire finie.

Il existe un moyen plus simple de créer des itérateurs en Python. Pour en savoir plus, visitez: Générateurs Python utilisant yield.

Articles intéressants...