web-dev-qa-db-fra.com

Quelle est la différence entre augmenter StopIteration et une déclaration de retour dans les générateurs?

Je suis curieux de savoir la différence entre l'utilisation de raise StopIteration et une instruction return dans les générateurs.

Par exemple, y a-t-il une différence entre ces deux fonctions?

def my_generator0(n):
    for i in range(n):
        yield i
        if i >= 5:
            return

def my_generator1(n):
    for i in range(n):
        yield i
        if i >= 5:
            raise StopIteration

Je suppose que la façon la plus "Pythonique" de le faire est la deuxième façon (veuillez me corriger si je me trompe), mais pour autant que je puisse voir les deux manières, lever une exception StopIteration.

38
slallum

Il n'est pas nécessaire d'augmenter explicitement StopIteration car c'est ce que fait une simple instruction return pour une fonction de générateur - alors oui, ce sont les mêmes. Mais non, utiliser simplement return est plus Pythonic.

De: http://docs.python.org/2/reference/simple_stmts.html#the-return-statement (valide pour Python 3.2)

Dans une fonction de générateur, l'instruction return n'est pas autorisée à inclure une expression_list. Dans ce contexte, un retour brut indique que le générateur est terminé et provoquera l'augmentation de StopIteration.

Ou comme le souligne @Bakuriu - la sémantique des générateurs a légèrement changé pour Python 3.3, donc ce qui suit est plus approprié:

Dans une fonction de générateur, l'instruction return indique que le générateur est terminé et provoquera l'augmentation de StopIteration. La valeur renvoyée (le cas échéant) est utilisée comme argument pour construire StopIteration et devient l'attribut StopIteration.value.

46
Jon Clements

À la fin de 2014, return est correct et raise StopIteration pour arrêter un générateur est sur un plan d'amortissement. Voir PEP 479 pour plus de détails.

Abstrait

Ce PEP propose une modification des générateurs: lorsque StopIteration est élevé dans un générateur, il est remplacé par RuntimeError. (Plus précisément, cela se produit lorsque l'exception est sur le point de sortir du cadre de pile du générateur.) Comme la modification est incompatible vers l'arrière, la fonctionnalité est initialement introduite à l'aide d'un __future__ déclaration.

Acceptation

Ce PEP a été accepté par le BDFL le 22 novembre…

Raisonnement

L'interaction des générateurs et StopIteration est actuellement quelque peu surprenante et peut masquer des bugs obscurs. Une exception inattendue ne doit pas entraîner un comportement subtilement modifié, mais doit provoquer un retraçage bruyant et facilement débogué. Actuellement, StopIteration déclenché accidentellement à l'intérieur d'une fonction de générateur sera interprété comme la fin de l'itération par la construction de boucle entraînant le générateur.

18
Blake Walsh

C'est vrai, ils sont équivalents sauf que l'un est lisible tandis que l'autre est obscur. Cela remonte à la toute première version des générateurs (PEP 255, sous "Spécification: Retour"), et les améliorations ultérieures de (telles que les coroutines) ne changent rien. yield from De 3.3 (PEP 380) étend cela à return <expr> Comme sucre syntaxique pour raise StopIteration(<expr>), mais cela ne change pas la signification de return;.

5
user395760