Quelle est la différence entre les itérateurs et les générateurs? Quelques exemples d'utilisation de chaque cas seraient utiles.
iterator
est un concept plus général: tout objet dont la classe a une next
méthode ( __next__
en Python 3) et une __iter__
méthode qui en a return self
.
Chaque générateur est un itérateur, mais pas l'inverse. Un générateur est construit en appelant une fonction qui a une ou plusieurs yield
expressions ( yield
instructions, en Python 2.5 et versions antérieures), et est un objet qui répond à la définition d'un iterator
.
Vous voudrez peut-être utiliser un itérateur personnalisé, plutôt qu'un générateur, lorsque vous avez besoin d'une classe avec un comportement de maintien d'état quelque peu complexe, ou souhaitez exposer d'autres méthodes en plus next
(et __iter__
et __init__
). Le plus souvent, un générateur (parfois, pour des besoins suffisamment simples, une expression de générateur ) est suffisant, et il est plus simple de coder car la maintenance de l'état (dans des limites raisonnables) est fondamentalement "faite pour vous" par la suspension et la reprise du cadre.
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 de générateur équivalent (genexp)
generator = (i*i for i in range(a, b))
prendrait plus de code à construire 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 3
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 la classe, Squares
vous pourriez facilement proposer des méthodes supplémentaires, à savoir
def current(self):
return self.start
si vous avez réellement besoin de telles fonctionnalités supplémentaires dans votre application.
for ... in ...:
, passé à une fonction, ou vous appellereziter.next()
for..in
syntaxe. Peut-être que je manquais quelque chose, mais c'était il y a quelque temps, je ne me souviens pas si j'ai résolu. Je vous remercie!En résumé: les itérateurs sont des objets qui ont une
__iter__
et une méthode__next__
(next
en Python 2). Les générateurs offrent un moyen simple et intégré de créer des instances d'itérateurs.Une fonction avec un rendement est toujours une fonction qui, lorsqu'elle est appelée, renvoie une instance d'un objet générateur:
Une expression de générateur renvoie également un générateur:
Pour une exposition et des exemples plus approfondis, continuez à lire.
Un générateur est un itérateur
Plus précisément, le générateur est un sous-type d'itérateur.
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 un rendement est une fonction qui, lorsqu'elle est appelée, renvoie un générateur:
Et un générateur, encore une fois, est un itérateur:
Un itérateur est un itérable
Un itérateur est un itérable,
qui nécessite une
__iter__
méthode qui retourne un Iterator:Quelques exemples d'itérables sont les tuples intégrés, les listes, les dictionnaires, les ensembles, les ensembles figés, les chaînes, les chaînes d'octets, les tableaux d'octets, les plages et les vues de mémoire:
Les itérateurs nécessitent une méthode
next
ou__next__
En Python 2:
Et en Python 3:
Nous pouvons obtenir les itérateurs des objets intégrés (ou objets personnalisés) avec la
iter
fonction:La
__iter__
méthode est appelée lorsque vous essayez d'utiliser un objet avec une boucle for. Ensuite, la__next__
méthode est appelée sur l'objet itérateur pour extraire chaque élément de la boucle. L'itérateur se lèveStopIteration
lorsque vous l'avez épuisé et il ne peut pas être réutilisé à ce stade.De la documentation
Dans la section Types génératrice de la section Types Itérateur des types intégrés documents :
(Je souligne.)
Nous apprenons donc que les générateurs sont un type (pratique) d'itérateur.
Exemples d'objets d'itérateur
Vous pouvez créer un objet qui implémente le protocole Iterator en créant ou en étendant votre propre objet.
Mais il est plus facile d'utiliser simplement un générateur pour ce faire:
Ou peut-être plus simple, une expression de générateur (fonctionne de manière similaire aux listes de compréhension):
Ils peuvent tous être utilisés de la même manière:
Conclusion
Vous pouvez utiliser le protocole Iterator directement lorsque vous devez étendre un objet Python en tant qu'objet pouvant être itéré.
Cependant, dans la grande majorité des cas, vous êtes le mieux placé
yield
pour définir une fonction qui renvoie un itérateur de générateur ou considérer les expressions de générateur.Enfin, notez que les générateurs offrent encore plus de fonctionnalités que les coroutines. J'explique Generators, ainsi que la
yield
déclaration, en détail sur ma réponse à "Que fait le mot-clé" yield "?".la source
Itérateurs:
Les itérateurs sont des objets qui utilisent la
next()
méthode pour obtenir la valeur suivante de la séquence.Générateurs:
Un générateur est une fonction qui produit ou produit une séquence de valeurs en utilisant
yield
méthode.Chaque
next()
méthode faisant appel à un objet générateur (par exemple:f
comme dans l'exemple ci-dessous) renvoyé par la fonction générateur (par exemple:foo()
fonction dans l'exemple ci-dessous), génère la valeur suivante en séquence.Lorsqu'une fonction de générateur est appelée, elle retourne un objet générateur sans même commencer l'exécution de la fonction. Lorsque la
next()
méthode 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 produite. Le rendement garde une trace de ie se souvient de la dernière exécution. Et le deuxièmenext()
appel continue à partir de la valeur précédente.L'exemple suivant illustre l'interaction entre le rendement et l'appel à la méthode suivante sur l'objet générateur.
la source
Ajouter une réponse car aucune des réponses existantes ne traite spécifiquement de la confusion dans la littérature officielle.
Les fonctions de générateur sont des fonctions ordinaires définies à l'aide de
yield
au lieu dereturn
. 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 a unenext()
méthode. Quand vous appeleznext()
, la valeur suivante fournie par la fonction générateur est renvoyée.La fonction ou l'objet peut être appelé le "générateur" selon le document source Python que vous lisez. Le glossaire Python dit les fonctions du générateur, tandis que le wiki Python implique les objets du générateur. Le tutoriel Python parvient remarquablement à impliquer les deux usages en l'espace de trois phrases:
Les deux premières phrases identifient les générateurs avec des fonctions génératrices, tandis que la troisième phrase les identifie avec des objets générateurs.
Malgré toute cette confusion, on peut rechercher la référence du langage Python pour le mot clair et final:
Ainsi, dans un usage formel et précis, "générateur" non qualifié signifie objet générateur, pas fonction générateur.
Les références ci-dessus sont pour Python 2 mais la référence du langage Python 3 dit la même chose. Cependant, le glossaire Python 3 indique que
la source
Tout le monde a une réponse très agréable et verbeuse avec des exemples et je l'apprécie vraiment. Je voulais juste donner une réponse en quelques lignes pour les personnes qui ne sont pas encore assez claires conceptuellement:
Si vous créez votre propre itérateur, il est un peu impliqué - vous devez créer une classe et au moins implémenter l'itérateur et les méthodes suivantes. Mais que se passe-t-il si vous ne voulez pas passer par ces tracas et que vous souhaitez créer rapidement un itérateur. Heureusement, Python fournit un moyen raccourci de définir un itérateur. Tout ce que vous avez à faire est de définir une fonction avec au moins 1 appel à céder et maintenant lorsque vous appelez cette fonction, elle renverra " quelque chose " qui agira comme un itérateur (vous pouvez appeler la méthode suivante et l'utiliser dans une boucle for). Ce quelque chose a un nom en Python appelé Generator
J'espère que cela clarifie un peu.
la source
Les réponses précédentes ont manqué cet ajout: un générateur a une
close
méthode, contrairement aux itérateurs typiques. Laclose
méthode déclenche uneStopIteration
exception dans le générateur, qui peut être interceptée dans unfinally
clause de cet itérateur, pour avoir la possibilité d'exécuter un nettoyage. Cette abstraction la rend plus utilisable dans les grands itérateurs que les simples. On peut fermer un générateur comme on pourrait fermer un fichier, sans avoir à se soucier de ce qui se trouve en dessous.Cela dit, ma réponse personnelle à la première question serait: itérable n'a qu'une
__iter__
méthode, les itérateurs typiques ont une__next__
méthode uniquement, les générateurs ont à la fois un__iter__
et un__next__
et un supplémentaireclose
.Pour la deuxième question, ma réponse personnelle serait: dans une interface publique, j'ai tendance à privilégier beaucoup les générateurs, car c'est plus résilient: la
close
méthode une plus grande composabilité avecyield from
. Localement, je peux utiliser des itérateurs, mais seulement si c'est une structure plate et simple (les itérateurs ne se composent pas facilement) et s'il y a des raisons de croire que la séquence est plutôt courte surtout si elle peut être arrêtée avant d'atteindre la fin. J'ai tendance à considérer les itérateurs comme une primitive de bas niveau, sauf comme des littéraux.Pour les questions de flux de contrôle, les générateurs sont un concept aussi important que promis: les deux sont abstraits et composables.
la source
__iter__
méthode, comment se fait-il qu'un itérateur puisse avoir__next__
seulement? S'ils sont censés être des itérables, je m'attendrais à ce qu'ils en aient nécessairement__iter__
aussi.__iter__
on iterables pour retourner un itérateur, qui ne nécessite qu'unenext
méthode (__next__
en Python3). Veuillez ne pas confondre les normes (pour le typage canard) avec leur implémentation (comment un interprète Python particulier l'a implémenté). C'est un peu comme la confusion entre les fonctions de générateur (définition) et les objets de générateur (implémentation). ;)Une fonction Generator est exactement comme une fonction régulière en Python mais elle contient une ou plusieurs
yield
instructions. Les fonctions de générateur sont un excellent outil pour créer des objets Iterator aussi facilement que possible. L' objet Iterator returend par générateur est également appelé objet Generator ou Generator .Dans cet exemple, j'ai créé une fonction Generator qui renvoie un objet Generator
<generator object fib at 0x01342480>
. Tout comme les autres itérateurs, les objets Generator peuvent être utilisés enfor
boucle ou avec la fonction intégréenext()
qui renvoie la valeur suivante du générateur.Une fonction de générateur est donc le moyen le plus simple de créer un objet Iterator.
Chaque objet générateur est un itérateur mais pas l'inverse. Un objet itérateur personnalisé peut être créé si sa classe implémente
__iter__
et__next__
méthode (également appelée protocole itérateur).Cependant, il est beaucoup plus facile d'utiliser la fonction des générateurs pour créer des itérateurs car ils simplifient leur création, mais un itérateur personnalisé vous donne plus de liberté et vous pouvez également implémenter d'autres méthodes en fonction de vos besoins, comme indiqué dans l'exemple ci-dessous.
la source
Exemples de Ned Batchelder fortement recommandés pour les itérateurs et les générateurs
Une méthode sans générateurs qui fait quelque chose pour les nombres pairs
tout en utilisant un générateur
return
déclarationL'appel de la
evens
méthode (générateur) est comme d'habitudeItérateur
et ce signet n'a rien d'autre à faire que de bouger
next
Pour utiliser Generator ... nous avons besoin d'une fonction
Pour utiliser Iterator ... nous avons besoin
next
etiter
Comme cela a été dit:
Tout l'avantage d'Iterator:
la source
Vous pouvez comparer les deux approches pour les mêmes données:
De plus, si vous vérifiez l'empreinte mémoire, le générateur prend beaucoup moins de mémoire car il n'a pas besoin de stocker toutes les valeurs en mémoire en même temps.
la source
J'écris spécifiquement pour les débutants en Python d'une manière très simple, bien qu'au fond Python fasse tellement de choses.
Commençons par le très basique:
Considérez une liste,
Écrivons une fonction équivalente:
o / p de
print(l): [1,2,3]
& o / p deprint(f()) : [1,2,3]
Rendons la liste l itérable: en python, la liste est toujours itérable, ce qui signifie que vous pouvez appliquer l'itérateur quand vous le souhaitez.
Appliquons l'itérateur sur la liste:
Faisons une fonction itérable, c'est-à-dire écrivons une fonction de générateur équivalente. En python dès que vous introduisez le mot-clé
yield
; il devient une fonction de générateur et l'itérateur sera appliqué implicitement.Remarque: Chaque générateur est toujours itérable avec un itérateur implicite appliqué et ici l'itérateur implicite est le nœud Donc la fonction du générateur sera:
Donc si vous avez observé, dès que vous avez fait fonction générateur fa, c'est déjà iter (f)
Maintenant,
C'est un peu que vous transformez int en int (x) qui est déjà int et il restera int (x).
Par exemple o / p de:
est
N'oubliez jamais que c'est Python et non C ou C ++
D'où la conclusion de l'explication ci-dessus:
la source