Quelle est la différence entre les itérateurs et les générateurs ? Il serait utile de donner quelques exemples d'utilisation de chaque cas.
Pour votre information, le rendement n'est pas une méthode, c'est un mot-clé.
Quelle est la différence entre les itérateurs et les générateurs ? Il serait utile de donner quelques exemples d'utilisation de chaque cas.
iterator
est un concept plus général : tout objet dont la classe a un __next__
méthode ( next
dans Python 2) et un __iter__
qui fait return self
.
Tout générateur est un itérateur, mais pas l'inverse. Un générateur est construit en appelant une fonction qui a un ou plusieurs yield
expressions ( yield
dans Python 2.5 et antérieurs), et est un objet qui répond à la définition du paragraphe précédent d'un iterator
.
Vous pouvez utiliser un itérateur personnalisé, plutôt qu'un générateur, lorsque vous avez besoin d'une classe dont le comportement de maintien d'état est quelque peu complexe, ou lorsque vous voulez exposer d'autres méthodes en dehors de __next__
(et __iter__
y __init__
). Le plus souvent, un générateur (parfois, pour des besoins suffisamment simples, un groupe électrogène expression ) est suffisant, et c'est plus simple à coder car le maintien de l'état (dans des limites raisonnables) est essentiellement "fait pour vous" par la suspension et la reprise de la trame.
Par exemple, un générateur tel que :
def squares(start, stop):
for i in range(start, stop):
yield i * i
generator = squares(a, b)
ou l'expression du générateur équivalente (genexp)
generator = (i*i for i in range(a, b))
demanderait plus de code pour être construit comme un itérateur personnalisé :
class Squares(object):
def __init__(self, start, stop):
self.start = start
self.stop = stop
def __iter__(self): return self
def __next__(self): # next in Python 2
if self.start >= self.stop:
raise StopIteration
current = self.start * self.start
self.start += 1
return current
iterator = Squares(a, b)
Mais, bien sûr, avec de la classe Squares
vous pourriez facilement proposer des méthodes supplémentaires, c'est-à-dire
def current(self):
return self.start
si vous avez réellement besoin d'une telle fonctionnalité supplémentaire dans votre application.
Quelle est la différence entre les itérateurs et les générateurs ? Il serait utile de donner quelques exemples d'utilisation de chaque cas.
En résumé : les itérateurs sont des objets qui ont un __iter__
et un __next__
( next
dans la méthode Python 2). Les générateurs offrent un moyen simple et intégré de créer des instances d'itérateurs.
Une fonction avec yield est toujours une fonction qui, lorsqu'elle est appelée, renvoie une instance d'un objet générateur :
def a_function():
"when called, returns generator object"
yield
Une expression de générateur renvoie également un générateur :
a_generator = (i for i in range(0))
Pour un exposé plus approfondi et des exemples, poursuivez votre lecture.
Plus précisément, le générateur est un sous-type d'itérateur.
>>> import collections, types
>>> issubclass(types.GeneratorType, collections.Iterator)
True
Nous pouvons créer un générateur de plusieurs façons. Une façon très courante et simple de le faire est d'utiliser une fonction.
Plus précisément, une fonction avec yield est une fonction qui, lorsqu'elle est appelée, renvoie un générateur :
>>> def a_function():
"just a function definition with yield in it"
yield
>>> type(a_function)
<class 'function'>
>>> a_generator = a_function() # when called
>>> type(a_generator) # returns a generator
<class 'generator'>
Et un générateur, encore une fois, est un Iterator :
>>> isinstance(a_generator, collections.Iterator)
True
Un Iterator est un Iterable,
>>> issubclass(collections.Iterator, collections.Iterable)
True
qui nécessite un __iter__
qui renvoie un Iterator :
>>> collections.Iterable()
Traceback (most recent call last):
File "<pyshell#79>", line 1, in <module>
collections.Iterable()
TypeError: Can't instantiate abstract class Iterable with abstract methods __iter__
Quelques exemples d'itérables sont les tuples intégrés, les listes, les dictionnaires, les ensembles, les ensembles figés, les chaînes de caractères, les chaînes d'octets, les tableaux d'octets, les plages et les vues mémoire :
>>> all(isinstance(element, collections.Iterable) for element in (
(), [], {}, set(), frozenset(), '', b'', bytearray(), range(0), memoryview(b'')))
True
next
o __next__
méthodeDans Python 2 :
>>> collections.Iterator()
Traceback (most recent call last):
File "<pyshell#80>", line 1, in <module>
collections.Iterator()
TypeError: Can't instantiate abstract class Iterator with abstract methods next
Et dans Python 3 :
>>> collections.Iterator()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Iterator with abstract methods __next__
Nous pouvons obtenir les itérateurs des objets intégrés (ou des objets personnalisés) avec la fonction iter
fonction :
>>> all(isinstance(iter(element), collections.Iterator) for element in (
(), [], {}, set(), frozenset(), '', b'', bytearray(), range(0), memoryview(b'')))
True
El __iter__
est appelée lorsque vous tentez d'utiliser un objet avec une boucle for. Ensuite, la méthode __next__
est appelée sur l'objet itérateur pour récupérer chaque élément de la boucle. L'itérateur lève StopIteration
lorsque vous l'avez épuisé, et il ne peut plus être réutilisé à ce moment-là.
Dans la section Types de générateurs de la section Types d'itérateurs de la section Types intégrés. documentation :
Python Les générateurs fournissent un moyen pratique de mettre en œuvre le protocole des itérateurs. Si l'objet conteneur
__iter__()
est implémentée en tant que générateur, elle renverra automatiquement un objet itérateur (techniquement, un objet générateur) fournissant à la méthode__iter__()
ynext()
[__next__()
dans les méthodes Python 3]. Vous trouverez de plus amples informations sur les générateurs dans la documentation relative à l'expression yield.
(C'est nous qui soulignons.)
Nous apprenons donc que les générateurs sont un type (pratique) d'itérateur.
Vous pouvez créer un objet qui implémente le protocole Iterator en créant ou en étendant votre propre objet.
class Yes(collections.Iterator):
def __init__(self, stop):
self.x = 0
self.stop = stop
def __iter__(self):
return self
def next(self):
if self.x < self.stop:
self.x += 1
return 'yes'
else:
# Iterators must raise when done, else considered broken
raise StopIteration
__next__ = next # Python 3 compatibility
Mais il est plus facile d'utiliser simplement un générateur pour faire cela :
def yes(stop):
for _ in range(stop):
yield 'yes'
Ou, peut-être plus simplement, une expression génératrice (fonctionne de manière similaire aux compréhensions de listes) :
yes_expr = ('yes' for _ in range(stop))
Ils peuvent tous être utilisés de la même manière :
>>> stop = 4
>>> for i, y1, y2, y3 in zip(range(stop), Yes(stop), yes(stop),
('yes' for _ in range(stop))):
... print('{0}: {1} == {2} == {3}'.format(i, y1, y2, y3))
...
0: yes == yes == yes
1: yes == yes == yes
2: yes == yes == yes
3: yes == yes == yes
Vous pouvez utiliser directement le protocole Iterator lorsque vous devez étendre un objet Python en tant qu'objet sur lequel il est possible d'effectuer une itération.
Cependant, dans la grande majorité des cas, il est préférable d'utiliser yield
pour définir une fonction qui renvoie un Iterator Generator ou considère des Expressions Generator.
Enfin, notez que les générateurs offrent encore plus de fonctionnalités que les coroutines. J'explique que les générateurs, ainsi que les yield
en profondeur sur ma réponse à la question "Que fait le mot clé "yield" ?".
Iterators :
Iterator sont des objets qui utilisent next()
pour obtenir la valeur suivante de la séquence.
Générateurs :
Un générateur est une fonction qui produit ou donne une séquence de valeurs à l'aide des éléments suivants yield
méthode.
Chaque next()
appel de méthode sur un objet générateur (par ex : f
comme dans l'exemple ci-dessous) renvoyée par la fonction du générateur (par ex : foo()
dans l'exemple ci-dessous), génère la valeur suivante dans la séquence.
Lorsqu'une fonction de générateur est appelée, elle renvoie un objet générateur sans même commencer l'exécution de la fonction. Lorsque next()
est appelée pour la première fois, la fonction commence à s'exécuter jusqu'à ce qu'elle atteigne l'instruction yield qui renvoie la valeur cédée. L'instruction yield garde la trace, c'est-à-dire qu'elle se souvient de la dernière exécution. Et la deuxième next()
l'appel se poursuit à partir de la valeur précédente.
L'exemple suivant démontre l'interaction entre le yield et l'appel à la méthode suivante sur un objet générateur.
>>> def foo():
... print "begin"
... for i in range(3):
... print "before yield", i
... yield i
... print "after yield", i
... print "end"
...
>>> f = foo()
>>> f.next()
begin
before yield 0 # Control is in for loop
0
>>> f.next()
after yield 0
before yield 1 # Continue for loop
1
>>> f.next()
after yield 1
before yield 2
2
>>> f.next()
after yield 2
end
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>
Ajout d'une réponse car aucune des réponses existantes ne traite spécifiquement de la confusion dans la littérature officielle.
Fonctions du générateur sont des fonctions ordinaires définies par yield
au lieu de return
. Lorsqu'elle est appelée, une fonction de générateur renvoie un objet générateur qui est une sorte d'itérateur - il possède un next()
méthode. Lorsque vous appelez next()
la valeur suivante produite par la fonction de générateur est retournée.
La fonction ou l'objet peut être appelé "générateur" selon le document source Python que vous lisez. Le site Glossaire Python dit les fonctions de générateur, tandis que le Python wiki implique des objets générateurs. Le site Tutoriel Python réussit remarquablement à impliquer les deux usages en l'espace de trois phrases :
Les générateurs sont un outil simple et puissant pour créer des itérateurs. Ils s'écrivent comme des fonctions ordinaires, mais utilisent l'instruction yield chaque fois qu'ils veulent renvoyer des données. Chaque fois que la fonction next() est appelée, le générateur reprend là où il s'est arrêté (il se souvient de toutes les valeurs des données et de la dernière instruction exécutée).
Les deux premières phrases identifient les générateurs aux fonctions de générateur, tandis que la troisième phrase les identifie aux objets de générateur.
En dépit de toute cette confusion, on peut chercher le Référence du langage Python pour le mot clair et définitif :
L'expression yield n'est utilisée que lors de la définition d'une fonction de générateur, et ne peut être utilisée que dans le corps d'une définition de fonction. Il suffit d'utiliser une expression yield dans une définition de fonction pour que cette définition crée une fonction de générateur au lieu d'une fonction normale.
Lorsqu'une fonction de générateur est appelée, elle renvoie un itérateur appelé générateur. Ce générateur contrôle ensuite l'exécution d'une fonction de générateur.
Donc, dans l'usage formel et précis, "générateur" non qualifié signifie objet générateur, et non fonction générateur.
Les références ci-dessus sont pour Python 2 mais Référence du langage Python 3 dit la même chose. Cependant, le Glossaire Python 3 déclare que
générateur ... Fait généralement référence à une fonction de générateur, mais peut faire référence à un itérateur de générateur dans certains contextes. Dans les cas où le sens voulu n'est pas clair, l'utilisation des termes complets évite toute ambiguïté.
Tout le monde a répondu de manière très claire et détaillée en donnant des exemples, et j'apprécie vraiment cela. Je voulais juste donner une réponse courte de quelques lignes pour les personnes qui ne sont pas encore tout à fait claires sur le plan conceptuel :
Si vous créez votre propre itérateur, c'est un peu compliqué. créer une classe et au moins implémenter les méthodes iter et next. Mais que se passe-t-il si vous ne voulez pas passer par cette étape fastidieuse et que vous souhaitez créer rapidement un itérateur ? Heureusement, Python offre un moyen rapide de définir un itérateur. Tout ce que vous avez à faire est de définir une fonction avec au moins un appel à yield et maintenant, lorsque vous appelez cette fonction, elle retournera " algo "qui agira comme un itérateur (vous pouvez appeler la méthode next et l'utiliser dans une boucle for). Ce algo a un nom en Python appelé Generator
J'espère que cela clarifie un peu les choses.
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.