Passons en revue les spécifications du langage autour des itérateurs et des générateurs Python

(J'ai essayé de m'inscrire tardivement au calendrier de l'Avent, mais il était déjà plein, donc je l'ai posté comme article régulier)

introduction

Je pensais connaître les spécifications du langage autour des itérateurs et des générateurs Python, et il y avait pas mal de fonctionnalités que j'avais ajoutées mais que je ne connaissais pas, donc je les ai résumées ici.

Dans cet article, «it» est traité comme une variable qui pointe vers un itérateur, et «Klass» est traité comme une classe définie par l'utilisateur sans préavis. Traitez «x» comme une variable pointant vers un objet.

Choses de base

It.next () en Python2, next (it) en Python3

La manière de récupérer l'élément suivant de l'itérateur a changé entre Python 2 et 3. En Python2 c'est ʻit.next () , en Python3 c'est next (it) . Si vous souhaitez implémenter vous-même une classe de type itérateur, vous pouvez implémenter Klass.next en Python2 et Klass .__ next__` en Python3. (Dans cet article, nous utiliserons le format Python3 ci-dessous.)

Un objet d'une classe qui implémente la méthode __iter__ qui retourne un itérateur est appelé un itérable.

Pour les objets itérables, vous pouvez créer un itérateur, tel que ʻiter (x) . Il peut également être spécifié après in dans une instruction for, ou sur le côté droit de l'opérateur in (l'opérateur in essaiera «__contains__» s'il existe, ou «__iter__» si ce n'est pas le cas). Les exemples d'itérables incluent les objets list, tuple, dict, str et file. L'itérateur lui-même est également itérable. Alternativement, l'objet de la classe qui implémente la méthode getitemest également itérable. Un itérateur ʻiter (x)créé à partir d'un objet d'une classe où __iter__ n'est pas implémenté et __getitem__ est implémenté est x [0], x [1] chaque fois que next est appelé. Il renvoie , ... et lève une exceptionStopIteration lorsque ʻIndexError` est levé.

Exception StopIteration à la fin de l'itérateur

Continuez avec next (it), et finalement une exceptionStopIteration sera levée lorsque l'élément suivant aura disparu. Lors de l'implémentation de Klass .__ next__, lancez une exceptionStopIteration s'il n'y a plus rien à retourner.

L'itérateur et le générateur sont différents

Un "générateur" est une fonction qui renvoie un itérateur, similaire à une fonction régulière, mais avec une instruction yield. Le générateur lui-même n'est pas un itérateur. De plus, une "expression de générateur" est une expression qui renvoie un itérateur, similaire à la notation d'inclusion de liste, mais entourée de cercles au lieu de carrés. iter(it) is it Quand ʻitest un itérateur, ʻiter (it)doit retourner ʻitlui-même. Autrement dit, si vous implémentez un itérateur, vous devriez dire quelque chose commeKlass .__ iter __ (self): return self. Dans l'instruction for, for x in it:etfor x in iter (it): sont censés être équivalents. Ce qui suit est un exemple de ce qui se passe lorsque ʻit et ʻiter (it) `sont différents.

it et iter(it)


print(sys.version)  # ==> 3.4.1 (default, May 23 2014, 17:48:28) [GCC]

# iter(it)Si le retourne
class Klass:
    def __init__(self):
        self.x = iter('abc')
    def __iter__(self):
        return self
    def __next__(self):
        return next(self.x)

it = Klass()
for x in it:
    print(x)  # ==> 'a', 'b', 'c'

# iter(it)Ne le retourne pas
class Klass2(Klass):
    def __iter__(self):
        return iter('XYZ')

it = Klass2()
for x in it:
    print(x)  # ==> 'X', 'Y', 'Z'
print(next(it))  # ==> 'a'

Saviez-vous cela?

Itérateur de format Iter (appelable, sentinelle)

Quand iter est appelé avec deux arguments, il renvoie toujours un itérateur, mais le comportement est très différent. S'il y a deux arguments, le premier argument doit être un objet appelable (une fonction ou un autre objet avec les méthodes call), non itérable. L'itérateur renvoyé par this appelle un appel sans argument à chaque fois qu'il appelle next. Lève une exception StopIteration si le résultat renvoyé est égal à sentinel. Si vous l'écrivez comme un générateur avec un pseudo-code, il se comportera ainsi.

2 argument iter


def iter_(callable, sentinel):
    while 1:
        a = callable()
        if a == sentinel:
            raise StopIteration
        else:
            yield a

Le [Document officiel] de Python (http://docs.python.jp/3/library/functions.html#iter) déclare qu'il est utile, par exemple, lors de la lecture d'un fichier jusqu'à ce qu'une ligne vide apparaisse.

Cité du document officiel


with open('mydata.txt') as fp:
    for line in iter(fp.readline, ''):
        process_line(line)

Envoyer la valeur au générateur.

Dans le générateur v = (yield x) Si vous écrivez comme, vous pouvez obtenir la valeur v lorsque le générateur redémarre. Si le générateur est redémarré normalement, v sera Aucun. Si vous appelez la méthode send au lieu de next, comme gen.send (a), le générateur redémarrera et v contiendra un fichier. Il retourne ensuite si la valeur est renvoyée, comme lors de l'appel suivant, et lève une exception StopIteration si rien n'est généré. Document officiel donne un exemple de compteur avec une fonction de changement de valeur.

Cité du document officiel


def counter(maximum):
    i = 0
    while i < maximum:
        val = (yield i)
        # If value provided, change counter
        if val is not None:
            i = val
        else:
            i += 1

Au fait. Vous ne pouvez pas utiliser l'envoi soudainement et vous devez faire la suite au moins une fois avant de pouvoir utiliser l'envoi. (TypeError: impossible d'envoyer une valeur non-None à un générateur qui vient de démarrer.) Comme vous pouvez voir d'où va la valeur envoyée, si elle n'a jamais été la suivante, la valeur va. Probablement parce qu'il n'y a pas de place.

Envoyer une exception au générateur de générateur.

generator.throw(type[, value[, traceback]]) Vous permet de déclencher une exception où le générateur a été interrompu. Si le générateur renvoie une valeur, il la renvoie et lève une exception StopIteration si rien ne donne. Si l'exception levée n'est pas traitée, elle sera propagée telle quelle. (Honnêtement, je ne peux penser à aucune utilisation efficace)

Fermer le générateur de générateur.

Déclenche une exception GeneratorExit où le générateur a été interrompu. Si une exception GeneratorExit ou StopIteration est levée, generator.close () s'arrête là. Si une valeur est renvoyée, un RuntimeError sera déclenché. Si le générateur était initialement fermé, ne faites rien. Ecrit en pseudo code, ça ressemble à ça?

generator.Traitement proche


def generator_close(gen):
    try:
        gen.throw(GeneratorExit)
    except (GeneratorExit, StopIteration):
        return
    throw RuntimeError

Je ne vois aucune utilité pour cela non plus. Il n'y a aucune garantie qu'il sera appelé, vous ne pouvez donc pas écrire quelque chose qui est censé être appelé. Je n'ai trouvé aucun document indiquant clairement cela, mais quand je romps avec l'instruction for, il semble qu'une exception GeneratorExit soit lancée au générateur.

Une exception de sortie du générateur se produit lors de la rupture avec une instruction for


def gen():
    for i in range(10):
        try:
            yield i
        except GeneratorExit:
            print("Generator closed.")
            raise

for i in gen():
    break  # ==> "Generator closed." is printed.

[3.3 ou version ultérieure] Syntaxe de délégation au sous-générateur

Vous pouvez renvoyer expr séquentiellement en écrivant yield from expr (expr est une expression qui renvoie un itérable) dans le générateur. Sans considérer l'envoi, les deux codes suivants sont équivalents.

Délégation au sous-générateur


def gen1():
    yield from range(10)
    yield from range(20)

def gen2():
    for i in range(10):
        yield i
    for i in range(20):
        yield i

S'il y a envoi, la valeur envoyée est transmise au sous-générateur.

Résumé

Les itérateurs sont souvent utilisés en Python, mais ils se sont souvent arrêtés à ce qu'ils pensaient savoir de leurs spécifications et de leurs anciennes connaissances. Par conséquent, j'ai examiné les spécifications linguistiques sur la base de la documentation officielle. Il y avait pas mal de choses que je ne savais pas, mais pour être honnête, la plupart d'entre elles ne proposent pas d'utilisations utiles. S'il y a quelque chose comme "Il y a d'autres spécifications comme celle-ci" ou "J'utilise cette fonction comme ça", veuillez l'écrire dans la section commentaires.

Recommended Posts

Passons en revue les spécifications du langage autour des itérateurs et des générateurs Python
Itérateur et générateur Python
[Python] Une compréhension approximative des itérables, des itérateurs et des générateurs
Générez des nombres de Fibonacci avec des fermetures, des itérateurs et des générateurs Python
Notation et générateur d'inclusion de liste Python
L'histoire de Python et l'histoire de NaN
[Python] Maîtrisons tout et tout
Revue des bases de Python (FizzBuzz)
Changer la saturation et la clarté des spécifications de couleur comme # ff000 dans python 2.5
[Blender x Python] Maîtrisons le matériel !!
Revoir le concept et la terminologie de la régression
Lisons le fichier RINEX avec Python ①
Résumons le standard de codage Python PEP8 (1)
Qu'en est-il de 2017 autour du langage Crystal? (Illusion)
Résumons le standard de codage Python PEP8 (2)
Communication socket par langage C et Python
Academia Potter et le mystérieux Python Pass
Python open et io.open sont les mêmes
J'ai comparé la vitesse de l'écho du framework web en langage go et du flask du framework web python
Jouons avec Python Receive et enregistrez / affichez le texte du formulaire de saisie
Imprimons un PDF avec python en utilisant foxit reader et spécifions l'imprimante en silence!