Selon docs, la fonction send ():

"Reprend l'exécution et" envoie "une valeur dans la fonction de générateur. L'argument de valeur devient le résultat de l'expression de rendement actuelle. La méthode send () renvoie la valeur suivante fournie par le générateur, ou déclenche StopIteration si le générateur se termine sans produire une autre valeur. Lorsque send () est appelé pour démarrer le générateur, il doit être appelé avec None comme argument, car aucune expression yield ne peut recevoir la valeur. "

Mais je ne comprends pas pourquoi "l'argument value devient le résultat de l'expression de rendement actuelle" ne se produit pas dans l'exemple suivant:

def gen():
    yield 1
    x = (yield 42)
    print(x)
    yield 2

>>>c=gen() #create generator
>>>next(c) #prints '1' and stop execution, which is caused by yield 1
>>>c.send(100) #prints '42', because 'The send() method returns the next value yielded by the generator'
>>>next(c) #prints 'None' and '2'

Alors pourquoi la variable x reste "Aucune" alors que je lui envoie 100 par c.send (100)? Il semble que l'expression yield dans la partie droite fonctionne en deux étapes: d'abord elle renvoie la valeur à l'appelant du générateur et le second retourne l'argument de la fonction d'envoi à l'intérieur du générateur. Et si vous ajoutez un autre suivant (c) avant d'envoyer (42), j'obtiendrai le comportement attendu et le programme affichera «100». Il n'est pas clair pour moi d'après la documentation, pourquoi ces deux étapes ne devraient pas se produire simultanément lorsque j'appelle send ().

1
xcix 11 août 2017 à 08:53

2 réponses

Meilleure réponse

Je pense que je l'ai compris.

c = gen()

Vous créez un générateur dans la variable c, là aucun code du générateur n'est exécuté.

next(c)

Ensuite, vous utilisez la fonction next(), la fonction next passe à l'instruction yield suivante qui est yield 1 ici. Donc ceci yield renvoie 1 et arrête l'exécution du générateur pour essayer d'attraper une valeur.

La ligne suivante est c.send(100), donc le yield 1 attendait une valeur et vous l'avez fournie, mais cette valeur n'est pas enregistrée.
La méthode send exécute également le reste du générateur jusqu'à la prochaine instruction yield qui est:

x = (yield 42)

Donc, le rendement retourne ici 42 et arrête le programme générateur pour essayer d'attraper une valeur. Mais après ça tu appelles

next(c)

Et vous n'avez pas fourni de valeur donc x est None maintenant. Ensuite, le reste du code est exécuté (jusqu'à la prochaine instruction yield, n'oubliez pas)

print(x)
yield 2

Donc, il imprime x qui est None, puis yield renvoie 2 et essaie d'attraper une valeur.

Essayez d'écrire ceci

c = gen()
next(c)
next(c)
c.send(100)

Et cela fonctionnera (comprenez pourquoi!)

2
Zcode 11 août 2017 à 08:48

Alors, pourquoi x variable reste 'None' alors que j'envoie 100 par c.send (100)?

Parce que le courant yield n'est pas celui que vous pensez qu'il est, c'est celui d'avant.

Lorsque vous envoyez 100, le générateur est toujours arrêté à yield 1, pas encore à yield 42, donc 100 sera le résultat de yield 1, pas de yield 42.

Pour voir cela plus clairement, si vous modifiez le générateur afin de récupérer le contenu de yield 1 dans une variable z, vous verrez que z contient 100:

>>> def gen():
...     z = yield 1
...     x = (yield 42)
...     print(x, z)
...     yield 2
... 
>>> c=gen()
>>> next(c)
1
>>> c.send(100)
42
>>> next(c)
None 100
2
>>>

Et c'est la raison pour laquelle un next() supplémentaire imprimera 100. Retour à votre code:

def gen():
    yield 1  # first next(), stops here (before "getting out" of yield)
    x = (yield 42) # second next(), stops here (before "getting out" of yield),
                   # so, when you send(100), then 100 is given to x and execution goes on, so:
    print(x)  # 100 is printed
    yield 2  # ... and 2.
4
zezollo 11 août 2017 à 08:35