web-dev-qa-db-fra.com

Retour et rendement dans la même fonction

Que se passe-t-il exactement lorsque le rendement et le retour sont utilisés dans la même fonction en Python, comme ceci?

def find_all(a_str, sub):
    start = 0
    while True:
        start = a_str.find(sub, start)
        if start == -1: return
        yield start
        start += len(sub) # use start += 1 to find overlapping matches

Est-ce toujours un générateur?

56
nekomimi

Oui, c'est toujours un générateur. return équivaut (presque) à augmenter StopIteration.

PEP 255 le précise:

Spécification: retour

Une fonction de générateur peut également contenir des instructions de retour de la forme:

"return"

Notez qu'une expression_list n'est pas autorisée sur les instructions de retour dans le corps d'un générateur (bien que, bien sûr, elles puissent apparaître dans les corps des fonctions non génératrices imbriquées dans le générateur).

Lorsqu'une instruction return est rencontrée, le contrôle se déroule comme dans tout retour de fonction, exécutant les clauses finally appropriées (le cas échéant). Une exception StopIteration est ensuite levée, signalant que l'itérateur est épuisé. Une exception StopIteration est également déclenchée si le contrôle s'écoule de l'extrémité du générateur sans retour explicite.

Notez que return signifie "J'ai terminé et je n'ai rien d'intéressant à retourner", pour les fonctions de générateur et les fonctions non génératrices.

Notez que return n'est pas toujours équivalent à augmenter StopIteration: la différence réside dans la façon dont les constructions try/except englobantes sont traitées. Par exemple,

>>> def f1():
...     try:
...         return
...     except:
...        yield 1
>>> print list(f1())
[]

car, comme dans toute fonction, return revient simplement à exit, mais

>>> def f2():
...     try:
...         raise StopIteration
...     except:
...         yield 42
>>> print list(f2())
[42]

parce que StopIteration est capturé par un "sauf", comme toute exception.

59
NPE

Oui, c'est toujours un générateur. Un return ou return None Vide peut être utilisé pour terminer une fonction de générateur. Cela équivaut à élever un StopIteration (voir réponse de @ NPE pour plus de détails).

Notez qu'un retour avec des arguments non-None est un SyntaxError dans les versions Python antérieures à 3.3.

Comme indiqué par @BrenBarn dans les commentaires à partir de Python 3.3, la valeur de retour est maintenant passée à StopIteration.

De PEP 38 :

Dans un générateur, l'instruction

return value

est sémantiquement équivalent à

raise StopIteration(value)
24
Ashwini Chaudhary

Il existe un moyen d'obtenir une méthode de rendement et de retour dans une fonction qui vous permet de renvoyer une valeur ou un générateur.

Il n'est probablement pas aussi propre que vous le souhaiteriez, mais il fait ce que vous attendez.

Voici un exemple:

def six(how_many=None):
    if how_many is None or how_many < 1:
        return None  # returns value

    if how_many == 1:
        return 6  # returns value

    def iter_func():
        for count in range(how_many):
            yield 6
    return iter_func()  # returns generator
8
William Rusnack