743 votes

Différence entre les générateurs et les itérateurs de Python

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.

15voto

Marwan Mostafa Points 134

Exemples de Ned Batchelder fortement recommandé pour les itérateurs et les générateurs

Une méthode sans générateurs qui font quelque chose aux nombres pairs

def evens(stream):
   them = []
   for n in stream:
      if n % 2 == 0:
         them.append(n)
   return them

alors qu'en utilisant un générateur

def evens(stream):
    for n in stream:
        if n % 2 == 0:
            yield n
  • Nous n'avons pas besoin liste ni un return déclaration
  • Efficace pour les flux de longueurs importantes/ infinies ... il se contente de marcher et de donner la valeur.

Appeler le evens La méthode (générateur) est comme d'habitude

num = [...]
for n in evens(num):
   do_smth(n)
  • Générateur également utilisé pour casser la double boucle

Itérateur

Un livre plein de pages est un itérable Un signet est un itérateur

et ce signet n'a rien à faire sauf à bouger next

litr = iter([1,2,3])
next(litr) ## 1
next(litr) ## 2
next(litr) ## 3
next(litr) ## StopIteration  (Exception) as we got end of the iterator

Pour utiliser Generator ... nous avons besoin d'une fonction

Pour utiliser l'Iterator ... nous avons besoin de next y iter

Comme cela a été dit :

Une fonction Generator renvoie un objet itérateur.

Tout l'intérêt de l'Iterator :

Stocker un élément à la fois en mémoire

14voto

jouell Points 850

L'antisèche No-code 4 lignes :

A generator function is a function with yield in it.

A generator expression is like a list comprehension. It uses "()" vs "[]"

A generator object (often called 'a generator') is returned by both above.

A generator is also a subtype of iterator.

12voto

Hibou57 Points 608

Les réponses précédentes ont omis cet ajout : un générateur a un close alors que les itérateurs classiques ne le font pas. Le site close déclenche un StopIteration dans le générateur, qui peut être attrapé dans une finally dans cet itérateur, pour avoir une chance d'effectuer un nettoyage. Cette abstraction le rend plus utilisable dans les itérateurs plus grands que 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 la suivante : itérable a un caractère __iter__ seulement, les itérateurs typiques ont une méthode __next__ uniquement, les générateurs ont à la fois une méthode __iter__ et un __next__ et un autre close .

Pour la deuxième question, ma réponse personnelle serait la suivante : dans une interface publique, j'ai tendance à privilégier les générateurs, car ils sont plus résilients : l'option close et une plus grande composabilité avec yield from . Localement, je peux utiliser des itérateurs, mais seulement s'il s'agit d'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 les promesses : les deux sont abstraits et composables.

9voto

irudyak Points 39

Il est difficile de répondre à la question sans deux autres concepts : iterable y iterator protocol .

  1. Quelle est la différence entre iterator y iterable ? Conceptuellement, vous itérez sur iterable à l'aide de l'outil correspondant iterator . Il y a quelques différences qui peuvent aider à distinguer iterator y iterable dans la pratique :

    • Une différence est que iterator a __next__ méthode, iterable ne le fait pas.
    • Une autre différence - les deux contiennent __iter__ méthode. En cas de iterable il renvoie l'itérateur correspondant. En cas de iterator il se retourne lui-même. Cela peut aider à distinguer iterator y iterable dans la pratique.

    x = [1, 2, 3] dir(x) [... iter ...] x_iter = iter(x) dir(x_iter) [... iter ... next ...] type(x_iter) list_iterator

  2. Quels sont les iterables en python ? list , string , range etc. Quels sont les iterators ? enumerate , zip , reversed etc. Nous pouvons vérifier cela en utilisant l'approche ci-dessus. C'est un peu confus. Il serait probablement plus simple de n'avoir qu'un seul type. Y a-t-il une différence entre range y zip ? L'une des raisons de faire cela - range a beaucoup de fonctionnalités supplémentaires - nous pouvons l'indexer ou vérifier s'il contient un certain nombre de chiffres, etc. aquí ).

  3. Comment pouvons-nous créer un iterator nous-mêmes ? En théorie, nous pouvons mettre en œuvre Iterator Protocol (voir aquí ). Nous devons écrire __next__ y __iter__ et soulever StopIteration et ainsi de suite (voir la réponse d'Alex Martelli pour un exemple et une motivation possible, voir aussi aquí ). Mais en pratique, nous utilisons des générateurs. Il semble que ce soit de loin la principale méthode pour créer des iterators en python .

Je peux vous donner quelques autres exemples intéressants qui montrent une utilisation quelque peu déroutante de ces concepts dans la pratique :

  • sur keras nous avons tf.keras.preprocessing.image.ImageDataGenerator ; cette classe n'a pas __next__ y __iter__ Il ne s'agit donc pas d'un itérateur (ou d'un générateur) ;
  • si vous appelez son flow_from_dataframe() vous obtiendrez DataFrameIterator qui a ces méthodes, mais il n'implémente pas StopIteration (ce qui n'est pas courant dans les itérateurs intégrés à l'application python ) ; dans la documentation, nous pouvons lire que "A DataFrameIterator donnant des tuples de (x, y) "Une fois de plus, la terminologie utilisée prête à confusion ;
  • nous avons également Sequence classe dans keras et c'est une implémentation personnalisée d'une fonctionnalité de générateur (les générateurs réguliers ne sont pas adaptés au multithreading) mais elle n'implémente pas __next__ y __iter__ il s'agit plutôt d'une enveloppe autour des générateurs (elle utilise les yield ) ;

8voto

N Randhawa Points 2888

Fonction de générateur, Objet de générateur, Générateur :

A Fonction de générateur est comme une fonction ordinaire en Python, mais elle contient un ou plusieurs éléments suivants yield déclarations. Le générateur de fonctions est un excellent outil pour créer Itérateur des objets aussi facilement que possible. Le site Itérateur L'objet renvoyé par la fonction de générateur est également appelé Objet générateur o Générateur .

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 dans un processus de for ou avec la fonction intégrée next() qui renvoie la valeur suivante du générateur.

def fib(max):
    a, b = 0, 1
    for i in range(max):
        yield a
        a, b = b, a + b
print(fib(10))             #<generator object fib at 0x01342480>

for i in fib(10):
    print(i)               # 0 1 1 2 3 5 8 13 21 34

print(next(myfib))         #0
print(next(myfib))         #1
print(next(myfib))         #1
print(next(myfib))         #2

Une fonction de générateur est donc le moyen le plus simple de créer un objet Iterator.

Itérateur :

Chaque objet générateur est un itérateur mais pas l'inverse. Un objet itérateur personnalisé peut être créé si sa classe met en œuvre les éléments suivants __iter__ y __next__ (également appelé protocole d'itération).

Cependant, il est beaucoup plus facile d'utiliser la fonction des générateurs pour créer itérateurs parce qu'ils simplifient leur création, mais un Iterator personnalisé vous donne plus de liberté et vous pouvez également implémenter d'autres méthodes selon vos besoins comme le montre l'exemple ci-dessous.

class Fib:
    def __init__(self,max):
        self.current=0
        self.next=1
        self.max=max
        self.count=0

    def __iter__(self):
        return self

    def __next__(self):
        if self.count>self.max:
            raise StopIteration
        else:
            self.current,self.next=self.next,(self.current+self.next)
            self.count+=1
            return self.next-self.current

    def __str__(self):
        return "Generator object"

itobj=Fib(4)
print(itobj)               #Generator object

for i in Fib(4):  
    print(i)               #0 1 1 2

print(next(itobj))         #0
print(next(itobj))         #1
print(next(itobj))         #1

Prograide.com

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.

Powered by:

X