web-dev-qa-db-fra.com

Pourquoi lève-t-il ensuite une 'StopIteration', mais 'pour' faire un retour normal?

Dans ce morceau de code, pourquoi utiliser for ne donne-t-il pas StopIteration ou la boucle for intercepte-t-elle toutes les exceptions puis se ferme-t-elle en silence? Dans ce cas, pourquoi avons-nous le return ?? Ou est le raise StopIteration causé par: return None?

#!/usr/bin/python3.1
def countdown(n):
    print("counting down")
    while n >= 9:
        yield n
        n -= 1
    return

for x in countdown(10):
    print(x)

c = countdown(10)
next(c)
next(c)
next(c)

En supposant que StopIteration soit déclenché par: return None. Quand est-ce que GeneratorExit est généré?

def countdown(n):
    print("Counting down from %d" % n)
    try:
        while n > 0:
            yield n
            n = n - 1
    except GeneratorExit:
        print("Only made it to %d" % n)

Si je fais manuellement un:

c = countdown(10)
c.close() #generates GeneratorExit??

Dans quel cas, pourquoi ne vois-je pas de trace?

55
user621819

La boucle for écoute explicitement StopIteration.

Le but de l'instruction for est de boucler sur la séquence fournie par un itérateur et l'exception est utilisée pour signaler que l'itérateur est maintenant terminé; for n'intercepte pas les autres exceptions déclenchées par l'objet qui est itéré, mais celle-là.

C'est parce que StopIteration est le signal normal, attendu, qui indique à qui que ce soit itératif qu'il n'y a plus rien à produire.

Une fonction de générateur est un type particulier d’itérateur; il soulève en effet StopIteration lorsque la fonction est terminée (c’est-à-dire lorsqu’elle revient, donc oui, return None soulève StopIteration). C'est une exigence des itérateurs. ils doivent lever StopIteration lorsqu'ils sont terminés; en fait, une fois qu'un StopIteration a été levé, il essaie d'obtenir un autre élément (via next(), ou en appelant la .next() (py 2) ou la fonction .__next__() (py 3) sur l’itérateur) doit toujours toujours relancer StopIteration.

GeneratorExit est une exception pour communiquer dans le sens autre . Vous êtes explicitement en train de fermer un générateur avec une expression yield et la manière Python communique cette fermeture Pour que le générateur se trouve en levant GeneratorExit à l'intérieur de cette fonction, vous capturez explicitement cette exception dans countdown, son objectif est de permettre à un générateur de nettoyer les ressources si nécessaire lors de la fermeture.

Un GeneratorExit n'est pas propagé à l'appelant; voir la generator.close() documentation .

75
Martijn Pieters