Python Iterators : présentation rapide de leur fonctionnement et de leur utilisation

Les Python Iterators sont des objets Python comportant un ensemble quantifiable d’éléments permettant d’implémenter le protocole de l’itérateur composé des deux fonctions iter() et next().

Python Iterators : de quoi s’agit-il exactement ?

Les Python Iterators sont d’abord une forme spéciale d’objets Python. Ils se distinguent par leur composition : ils sont en effet constitués d’un ensemble quantifiable d’éléments. Il est donc possible de compter les éléments d’un itérateur et d’itérer sur tous les éléments d’un Python Iterator.

Conseil

Python est un excellent langage de programmation, tout particulièrement adapté au développement de projets Web. Faites appel à l’outil Deploy Now pour votre projet Web et profitez de l’environnement de création et de déploiement automatiques de GitHub, pour bénéficier d’une vue d’ensemble sur votre projet à tout moment.

Iterators ou Iterables ?

Il convient de ne pas confondre un Python Iterator avec un Python Iterable, même si ces deux éléments sont étroitement liés. Les différents itérables, comme la liste Python, se distinguent par l’utilisation d’une méthode iter(), par l’intermédiaire de laquelle ils peuvent itérer. Pour ne pas les confondre, vous pouvez raisonner ainsi : tous les éléments positionnés à droite de l’en-tête de boucle au moment de l’appel d’une boucle for sont des itérables.

l = [1, 2, 3]
for x in l:
	print(x)
Python

Comme vous pouvez le voir, la liste enregistrée dans la variable « l » apparaît à droite de l’en-tête de la boucle, après le mot-clé « in », lors de l’appel de la boucle for en Python ; il s’agit donc d’un itérable.

Il est possible de dériver un Python Iterator d’un Python Iterable. Vous trouverez ci-dessous un exemple de code pour clarifier cet état de fait et mieux comprendre les différences entre les deux éléments :

string = "test"
iteratorobject = iter(string)
next(iteratorobject)
iter(iteratorobject)
Python

Dans le code, une chaîne de caractères Pythoncomportant la valeur test est enregistrée dans la variable appelée « string ». Les chaînes de caractères relèvent elles aussi de la catégorie des Iterables, car une itération peut s’effectuer par l’intermédiaire de chaque lettre de la chaîne de caractères.

Le fait que les chaînes de caractères permettent des itérations sous-entend en réalité que ces chaînes prennent également en charge la fonction iter(). Cet état de fait est illustré dans la ligne de code suivante, où nous créons un Python Iterator en appelant la fonction iter() en même temps que la chaîne de caractères précédemment créée. Celle-ci renvoie un pointeur vers la chaîne de caractères, qu’elle enregistre dans la variable appelée « iteratorobject ». Le Python Iterator se trouve donc dans un état donné. Cet état peut être modifié par l’appel de la méthode next() sur le Python Iterator. La méthode permet de déplacer le pointeur d’un caractère, de manière que la valeur iteratorobject cible la première lettre de la chaîne de caractères après l’appel de fonction.

L’appel de la fonction iter(), qui utilise le Python Iterator en tant que paramètre de transfert, renvoie une référence à celui-ci. Les Python Iterators sont donc auto-itérables.

Iterators ou Generators ?

Il est également essentiel d’établir une distinction entre les Python Iterators et les Python Generators. Si chaque Python Generator est un Python Iterator, l’inverse n’est pas forcément vrai. En effet, contrairement à un Python Generator, un Python Iterator n’est pas nécessairement créé avec une fonction comportant une expression yield.

Conseil

Les Python Iterators relèvent d’une technique de programmation avancée, qui n’est pas explicitée dans tous les tutoriels Python. Si vous souhaitez découvrir d’autres techniques avancées en lien avec Python, nous vous conseillons de vous intéresser aux articles suivants :

Utilisation des Python Iterators : pourquoi et dans quel but ?

Bien entendu, les Python Iterators sont avant tout utilisés à des fins d’itération. Le principal avantage des Python Iterators réside dans leur fonctionnement, basé sur le principe d’une «lazy evaluation» (littéralement « évaluation paresseuse »). En d’autres termes, chaque élément d’un Python Iterator peut être traité de façon individuelle sans qu’il soit nécessaire de charger intégralement la structure de données en mémoire. Cette solution comporte des avantages en matière d’efficacité, surtout si vous devez traiter d’importants volumes de données où un seul élément doit être chargé à la fois.

Python Iterators : comment les créer ?

Vous pouvez facilement créer votre propre Python Iterator. Pour ce faire, ajoutez simplement les fonctions iter() et next() à un objet Python ; cela vous permet de créer facilement un Python Iterator qui renvoie tous les nombres pairs. L’exemple ci-dessous illustre la marche à suivre :

class nombrespairs:
	def __iter__(self):
		self.x = 0
		return self
	def __next__(self):
		a = self.x
		self.x += 2
		return a
testobject = nombrespairs()
testiterator = iter(testobject)
print(next(testiterator))
print(next(testiterator))
print(next(testiterator))
print(next(testiterator))
Python

Pour créer un Python Iterator, vous devez commencer par créer une classe. Ici, elle est appelée « nombrespairs ». Au sein de cette classe, il convient ensuite d’implémenter les deux fonctions iter() et next(), chacune adoptant le comportement souhaité.

Dans notre cas, la fonction iter() se contente de renvoyer une référence au Python Iterator devant héberger la suite de nombres entiers (en commençant par 0). La logique de l’itération, c’est-à-dire le fait de ne renvoyer qu’un nombre sur deux (et donc un nombre pair) est prise en charge par la fonction next().

Une fois la classe définie, un objet de celle-ci est créé et enregistré au sein de la variable portant le nom « testobject ». Cet objet devient un Python Iterator dès lors que la fonction iter() est appelée, comme nous l’avons vu dans l’exemple de code précédent. Quatre appels de la fonction next() sont ensuite effectués, et leurs résultats s’affichent à l’écran. Le résultat de la section de code ci-dessus se présente de la manière suivante :

0
2
4
6

Python Iterators : comment les limiter ?

Le Python Iterator responsable de l’itération sur les nombres pairs pourrait s’exécuter à l’infini, les nombres pairs n’ayant techniquement pas de fin ; il convient donc de le limiter. Pour ce faire, vous pouvez combiner une déclaration StopIteration et une déclaration if…else en Python. Imaginons par exemple que vous souhaitiez afficher dans votre Python Iterator tous les nombres pairs jusqu’à 100 (inclus). L’exemple de code ci-dessous vous aiguille sur la marche à suivre :

class nombrespairs:
	def __iter__(self):
		self.x = 0
		return self
	def __next__(self):
		if self.x <= 100:
			a = self.x
			self.x += 2
			return a	
		else:
			StopIteration
testobject = nombrespairs()
testiterator = iter(testobject)
print(next(testiterator))
print(next(testiterator))
print(next(testiterator))
print(next(testiterator))
Python

Dans le code, rien n’a changé, si ce n’est la fonction d’implémentation next(). Ici, une déclaration if…else supplémentaire a été ajoutée. Elle s’assure que chaque nombre traité est bien inférieur ou égal à 100 et l’itération dans l’ensemble des nombres pairs ne peut se poursuivre qu’à cette condition. Dès que le nombre traité dépasse 100, l’appel de la déclaration StopIteration renvoie une erreur.