Les itérateurs et générateurs en Python

La boucle for en python ne fonctionne pas comme dans la majorité des autres langages de programmation. Elle permet de parcourir les objets “itérables”, c’est à dire, de manière générale, les chaines, les listes, les tuples et les dictionnaire. En réalité, la boucle for à besoin d’un objet iterator pour fonctionner. Cet objet possède une méthode next() qui renvoit à tour de rôle chacunes des valeurs que doit renvoyer l’objet pour chaque itération, et déclenche une exception StopIteration quand il arrive au bout. Petit exemple avec une liste :

>>> liste = ['Python', 'c\'est', 'bon']
>>> iterateur = liste.__iter__()
>>> iterateur
<listiterator object at 0x808968c>
>>> iterateur.next()
'Python'
>>> iterateur.next()
"c'est"
>>> iterateur.next()
'bon'
>>> iterateur.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

Comme vous pouvez le constater, chaque liste possède une méthode __iter__() qui retourne un objet iterator. Cet objet peut aussi être obtenu grace à la primitive iter(objet). On peut donc conclure qu’un objet itérable est un objet qui possède une méthode __iter__() qui retourne elle même un objet iterator.

En python, on ne se soucie pas de savoir si tel objet est de tel type, mais plutôt de savoir si l’on peut faire telle chose avec tel objet. On appel cela le duck typing : “Si ça marche comme un canard, que ça fait coin coin comme un canard, alors on peut appeler ça un canard”. Il est donc possible de créer très simplement un itérateur, et par extension un objet itérable :

>>> class Iterateur:
...     i = 0
...     def next(self):
...             if self.i >= 10: raise StopIteration
...             self.i += 1
...             return 2**self.i
...     def __iter__(self): return self
...
>>> objet_iterateur = Iterateur()
>>> for i in objet_iterateur: print i
...
1
2
4
8
16
32
64
128
256
512

Les générateurs

Les générateurs sont une manière de créer des itérateurs. Il existe deux moyens d’utiliser les générateurs : au travers d’une fonction en utilisant le mot clé yield au lieu de return et en utilisant les “generator expressions” qui ressemblent à la définition de liste par “list comprehension”.

Puisqu’un petit exemple vaut mieux qu’un beau discours :

>>> def iterateur():
...     for i in xrange(10):
...             yield 2**i
...
>>> for i in iterateur(): print i
...
1
2
4
8
16
32
64
128
256
512

Et de manière plus concise avec les “generator expressions” (voir mon billet sur la compréhension de liste pour voir des exemples de la syntaxe générale, le seul différence résidant dans l’utilisation des parenthèses au lieu des crochets) :

>>> for i in (2**x for x in xrange(10)): print i
...
1
2
4
8
16
32
64
128
256
512

Comprendre les itérateurs et les générateurs permet de rendre son code plus concis et de mieux comprendre le fonctionnement de Python avec les objets itérables.

A voir

Le Samedi 26 janvier 2008 à 18h45 Developpement Tags : ,

Commentaires RSS feed

Avatar de l'auteur

commentaire de Fred

mai 21st, 2008 at 9:23

il y a un bug


... def next(self):
... self.i += 1
... if self.i >= 10: raise StopIteration
... return 2**self.i

ça marchera mieux si la vérification intervient juste avant l’appel
sinon bon article!

Poster un commentaire


Ne sera pas publiée, elle est utile pour les Gravatars et la modération des commentaires.


Facultatif.


Vous pouvez utiliser ces marqueurs : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>