84 votes

Pour x dans y(): comment cela fonctionne-t-il?

J'étais à la recherche de code pour faire tourner un curseur dans le terminal et j'ai trouvé ceci. Je me demandais ce qui se passait dans le code. En particulier for c in spinning_cursor(): je n'ai jamais vu cette syntaxe. Est-ce parce que je retourne un élément à la fois à partir d'un générateur avec yield, et que cela est assigné à c ? D'autres exemples de cette utilisation for x in y() ?

import sys
import time

def spinning_cursor():
    cursor='/-\|'
    i = 0
    while 1:
        yield cursor[i]
        i = (i + 1) % len(cursor)

for c in spinning_cursor():
    sys.stdout.write(c)
    sys.stdout.flush()
    time.sleep(0.1)
    sys.stdout.write('\b')

54voto

Martijn Pieters Points 271458

En utilisant yield, une fonction devient un générateur. Un générateur est un type spécialisé d'itérateur. for boucle toujours sur des objets itérables, prenant chaque élément tour à tour et l'assignant au(x) nom(s) que vous avez listé(s).

spinning_cursor() renvoie un générateur, le code à l'intérieur de spinning_cursor() n'est pas exécuté tant que vous ne commencez pas à itérer sur le générateur. Itérer sur un générateur signifie que le code dans la fonction est exécuté jusqu'à ce qu'il rencontre une instruction yield, à ce moment-là le résultat de l'expression est retourné comme valeur suivante et l'exécution est à nouveau en pause.

La boucle for fait exactement cela, elle appellera l'équivalent de next() sur le générateur, jusqu'à ce que le générateur signale qu'il a terminé en levant StopIteration (ce qui se produit lorsque la fonction se termine). Chaque valeur de retour de next() est assignée, tour à tour, à c.

Vous pouvez voir cela en créant le générateur dans l'invite Python :

>>> def spinning_cursor():
...     cursor='/-\|'
...     i = 0
...     while 1:
...         yield cursor[i]
...         i = (i + 1) % len(cursor)
... 
>>> sc = spinning_cursor()
>>> sc

>>> next(sc)
'/'
>>> next(sc)
'-'
>>> next(sc)
'\\'
>>> next(sc)
'|'

Ce générateur spécifique ne se termine jamais, donc StopIteration n'est jamais levée et la boucle for continuera indéfiniment à moins que vous ne tuiez le script.

Une alternative bien plus ennuyeuse (mais plus efficace) serait d'utiliser itertools.cycle():

from itertools import cycle

spinning_cursor = cycle('/-\|')

5voto

aymericbeaumet Points 1692

En Python, l'instruction for vous permet d'itérer sur des éléments.

En fonction de la documentation :

L'instruction for de Python itère sur les éléments de n'importe quelle séquence (une liste ou une chaîne de caractères), dans l'ordre où ils apparaissent dans la séquence

Ici, l'élément sera la valeur de retour de spinning_cursor().

3voto

thegrinner Points 4638

La syntaxe for c in spinning_cursor() est une boucle for-each. Elle va itérer sur chaque élément dans l'itérateur retourné par spinning_cursor().

L'intérieur de la boucle fera :

  1. Écrire le caractère sur la sortie standard et vider le buffer pour qu'il s'affiche.
  2. Dormir pendant un dixième de seconde
  3. Écrire \b, qui est interprété comme un retour arrière (supprime le dernier caractère). Remarquez que cela se produit à la fin de la boucle pour qu'il ne soit pas écrit lors de la première itération, et qu'il partage l'appel à vider le buffer de l'étape 1.

spinning_cursor() va retourner un générateur, qui ne s'exécute pas réellement tant que vous ne commencez pas à itérer. Il semble qu'il va boucler à travers '/-\|', dans l'ordre, à l'infini. C'est un peu comme avoir une liste infinie sur laquelle itérer.

Ainsi, la sortie finale va être un spinner ASCII. Vous verrez ces caractères (au même endroit) se répéter jusqu'à ce que vous arrêtiez le script.

/
-
\
|

2voto

beiller Points 1903

La fonction spinning_cursor renvoie un itérable (un générateur à partir de yield).

for c in spinning_cursor():

serait la même chose que

 for i in [1, 2, 3, 4]:

2voto

Marwan Alsabbagh Points 4245

L'explication de Martijn Pieters est excellente. Voici une autre implémentation du même code que vous aviez dans la question. Il utilise itertools.cycle pour produire le même résultat que spinning_cursor. itertools est rempli d'excellents exemples d'itérateurs et de fonctions pour vous aider à créer vos propres itérateurs. Cela pourrait vous aider à mieux comprendre les itérateurs.

import sys, time, itertools

for c in itertools.cycle('/-\|'):
    sys.stdout.write(c)
    sys.stdout.flush()
    time.sleep(0.1)
    sys.stdout.write('\b')

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X