1
votes

En python quel est le but d'avoir un itérable et un itérateur, deux objets séparés?

Je sais que passer iterable comme argument à la méthode iter () renvoie un itérateur. Alors pourquoi ne peut-il pas être toujours un itérateur. Quel est le but d'avoir un objet itérable s'il n'a pas de méthode __next__ ?


0 commentaires

4 Réponses :


1
votes

Vous ne pouvez parcourir un itérateur qu'une seule fois. Considérez les itérateurs comme des fonctions qui renvoient les objets un par un. Vous ne pouvez les parcourir qu'une seule fois et vous devez les parcourir dans l'ordre prédéfini.

Les itérables sont des objets sur lesquels vous pouvez parcourir, mais contrairement aux itérateurs, ils ne sont pas affectés par l'itération et sont accessibles par d'autres moyens. Il est possible d'indexer dans un itérable, mais pas dans un itérateur. Cela signifie que je peux accéder au dixième, septième ou dernier élément d'un itérable sans avoir besoin d'autres éléments, mais je dois parcourir les éléments précédents d'un itérateur pour accéder à ces éléments.

Une explication plus approfondie peut être trouvée à cette réponse à une question similaire question.


0 commentaires

2
votes

Pensez à l'itérable comme au talent spécial d'un objet. Il peut être répété, par ex. lors de l'utilisation de la boucle for ou de la décompression .

Un itérateur est un objet qui est chargé de fournir des données à partir de quelque chose. Cela signifie que plusieurs de ces objets peuvent tous fournir des données indépendantes à partir du même objet sous-jacent.


0 commentaires

0
votes

Les classes décident de la manière dont elles seront itérées en fonction de ce qui est renvoyé par la méthode __iter__ . Parfois, les itérables sont leur propre itérateur (par exemple, un objet fichier) et parfois les itérables créent des objets itérateurs séparés (par exemple, une liste). C'est au développeur de décider quelle implémentation est la meilleure.

Dans le cas d'un objet fichier, il n'a qu'une seule position actuelle et les lectures continueront toujours à ce point. Cela n'a pas de sens d'avoir des itérateurs uniques qui devraient continuellement changer de position de fichier pour lire correctement. De même avec les protocoles de streaming qui ne peuvent pas du tout rembobiner.

Les générateurs sont comme des objets de fichier et des flux. Ils ne peuvent pas changer de position pour pouvoir être leur propre itérateur.

Pour un objet de liste, cependant, il serait étrange qu'une seule entité de code puisse l'itérer à la fois. Les objets list renvoient un itérateur séparé qui suit la position actuelle dans la liste pour cet itérateur uniquement.

La différence entre ces deux approches d'itération peut casser le code, ou du moins le rendre moins utilisable. Considérez un processeur de fichiers qui fonctionne avec des enregistrements multilignes. Il pourrait utiliser un interne pour continuer à itérer les lignes du fichier.

def file_processor(f):
    iter_f = iter(f)
    for line in iter_f:
        if line.startswith('newrecord'):
            for line in iter_f:
                print(line,strip())
                if line.startswith('endrecord'):
                    break

Mais cela s'arrête si vous passez dans une liste car cet interne pour commencera en haut de la liste de nouveau. Vous pouvez le modifier pour qu'il fonctionne avec plus d'objets en lui donnant explicitement un itérateur

def file_processor(f):
    for line in f:
        if line.startswith('newrecord'):
            for line in f:
                print(line,strip())
                if line.startswith('endrecord'):
                    break


0 commentaires

0
votes

À titre d'exemple d'itérable qui n'est pas lui-même un itérateur, prenons une liste. Un itérateur sur une liste doit contenir l'état, à savoir le numéro d'index de l'élément suivant à extraire. Une liste elle-même ne contient pas cet état. Mais regardons un exemple où nous avons une liste, générons un itérateur à partir de celle-ci et utilisons-le à la place de la liste, afin de démontrer comment le code fonctionnant autrement se briserait si une liste était elle-même un itérateur.

La clé Le problème est que nous parcourons la liste plus d'une fois. Dans cet exemple, les boucles sont imbriquées, mais des problèmes similaires se produiraient si les boucles étaient rencontrées séquentiellement.

Brontolo looks at Cucciolo
Brontolo looks at Dotto
Brontolo looks at Eolo
Brontolo looks at Gongolo
Brontolo looks at Mammolo
Brontolo looks at Pisolo

Sortie:

names = ["Brontolo", "Cucciolo", "Dotto", "Eolo",
         "Gongolo", "Mammolo", "Pisolo"]  # This is not an iterator...

names = iter(names)  # ... but let's simulate what would happen if it was.

for name1 in names:
    for name2 in names:
        if name1 == name2:
            print(f"{name1} looks in the mirror")
        else:
            print(f"{name1} looks at {name2}")

Cela ne fonctionne pas du tout correctement, car les deux boucles partagent le même itérateur. Lors de la première itération de la boucle externe name1 , l'index est incrémenté. Ensuite, la boucle interne name2 manque le premier élément et effectue une boucle du deuxième au dernier élément. Ensuite, à la prochaine tentative d'itération de la boucle externe, l'index pointe déjà vers la fin de la liste et la boucle se termine.

Maintenant, commentez le names = iter (names) code>, et bien sûr cela fonctionne comme prévu. Ce qui se passe cette fois, c'est que parce qu'une liste n'a pas de méthode __next__ , lorsqu'une instruction comme for name1 in names: est rencontrée, un un nouvel itérateur est généré à la volée pour donner les valeurs de name1 , et c'est cet itérateur qui contient l'index, plutôt que la liste elle-même. À chaque itération de la boucle externe, un objet itérateur entièrement séparé est généré de la même manière pour la boucle interne, qui peut ensuite être itérée indépendamment.


0 commentaires