599 votes

Pourquoi python utilise-t-il 'else' après les boucles for et while ?

Je comprends comment cette construction fonctionne :

for i in range(10):
    print(i)

    if i == 9:
        print("Too big - I'm giving up!")
        break;
else:
    print("Completed successfully")

Mais je ne comprends pas pourquoi else est utilisé comme mot-clé ici, puisqu'il suggère que le code en question ne s'exécute que si l'option for Le bloc ne se complète pas, ce qui est le contraire de ce qu'il fait ! Quelle que soit la façon dont j'y pense, mon cerveau ne peut pas progresser de façon transparente de l'élément for à la else bloc. Pour moi, continue ou continuewith serait plus logique (et j'essaie de m'entraîner à le lire comme tel).

Je me demande comment les codeurs Python lisent cette construction dans leur tête (ou à haute voix, si vous voulez). Peut-être ai-je raté quelque chose qui rendrait ces blocs de code plus facilement déchiffrables ?

38 votes

Vous pourriez le traduire par "alors" dans votre tête.

86 votes

N'oubliez pas la phrase clé du Zen de Python : "... ce chemin peut ne pas être évident au premier abord, à moins d'être néerlandais."

66 votes

Dans ma tête, je le traduis en "si pas de rupture" . Et, puisque break est très utilisé dans "Je l'ai trouvé" boucles, vous pouvez le traduire en "si non trouvé" ce qui n'est pas loin de ce que else lit

744voto

Lance Helsten Points 1122

Une construction commune consiste à exécuter une boucle jusqu'à ce que quelque chose soit trouvé, puis à sortir de la boucle. Le problème est que si je sors de la boucle ou si la boucle se termine, je dois déterminer quel cas s'est produit. Une méthode consiste à créer un indicateur ou une variable de stockage qui me permettra d'effectuer un deuxième test pour voir comment la boucle a été quittée.

Par exemple, supposons que je doive effectuer une recherche dans une liste et traiter chaque élément jusqu'à ce qu'un élément indicateur soit trouvé, puis arrêter le traitement. Si l'élément indicateur est manquant, une exception doit être levée.

Utilisation de l'outil Python for ... else la construction que vous avez

for i in mylist:
    if i == theflag:
        break
    process(i)
else:
    raise ValueError("List argument missing terminal flag.")

Comparez cela à une méthode qui n'utilise pas ce sucre syntaxique :

flagfound = False
for i in mylist:
    if i == theflag:
        flagfound = True
        break
    process(i)

if not flagfound:
    raise ValueError("List argument missing terminal flag.")

Dans le premier cas, le raise est étroitement liée à la boucle for avec laquelle elle fonctionne. Dans la seconde, la liaison n'est pas aussi forte et des erreurs peuvent être introduites lors de la maintenance.

102 votes

Cela l'explique mieux que la réponse choisie où l'auteur ne comprend pas vraiment ce que signifie for-else !

30 votes

Je dois dire que ce sucre syntaxique pourrait pourrir les dents de votre projet. Cela ne ferait pas un Python: the good parts livre.

1 votes

Pouvez-vous le confirmer dans votre exemple ? process(i) se produit pour chaque élément dans mylist strictement avant theflag et non à theflag elle-même ? Est-ce que c'est ce qui était prévu ?

359voto

Björn Lindqvist Points 3739

C'est une construction étrange, même pour les codeurs Python chevronnés. Lorsqu'elle est utilisée en conjonction avec des boucles for, elle signifie essentiellement "trouver un élément dans l'itérable, sinon si aucun n'a été trouvé, faire ...". Comme dans :

found_obj = None
for obj in objects:
    if obj.key == search_key:
        found_obj = obj
        break
else:
    print('No object found.')

Mais à chaque fois que vous voyez cette construction, une meilleure alternative est d'encapsuler la recherche dans une fonction :

def find_obj(search_key):
    for obj in objects:
        if obj.key == search_key:
            return obj

Ou utilisez une liste de compréhension :

matching_objs = [o for o in objects if o.key == search_key]
if matching_objs:
    print('Found {}'.format(matching_objs[0]))
else:
    print('No object found.')

Elle n'est pas sémantiquement équivalente aux deux autres versions, mais fonctionne suffisamment bien dans du code non critique en termes de performances où il importe peu d'itérer la liste entière ou non. D'autres peuvent ne pas être d'accord, mais personnellement j'éviterais d'utiliser les blocs for-else ou while-else dans du code de production.

Voir aussi [Python-ideas] Résumé des fils for...else

63 votes

La compréhension de la liste n'est pas la bonne solution. Si vous cherchez un seul élément, comme dans le cas de l'option for et que vous souhaitez utiliser un générateur d'expression/compréhension de liste, alors vous voulez next((o for o in objects if o.key == search_key), None) ou l'envelopper dans un try / except et n'utiliser aucune valeur par défaut au lieu d'un if / else .

4 votes

Et comme la réponse de Lance Helsten, il y a des cas réels où il est préférable d'utiliser un for/else construire.

5 votes

Santé. J'avais un fichier mal indenté où un else a été jumelé avec un for et je ne savais pas que c'était légal.

211voto

AirThomas Points 1489

Il y a une excellente présentation de Raymond Hettinger, intitulée Transformer le code en un Python beau et idiomatique dans lequel il aborde brièvement l'histoire de l'Union européenne. for ... else construire. La section concernée est "Distinguer les points de sortie multiples dans les boucles". à partir de 15:50 et continue pendant environ trois minutes. Voici les points forts :

  • Le site for ... else a été conçu par Donald Knuth pour remplacer certaines fonctions de l'ordinateur. GOTO les cas d'utilisation ;
  • Réutilisation de la else Le mot clé avait un sens parce que "c'est ce que Knuth utilisait, et les gens savaient, à l'époque, tout [ for déclarations] avaient intégré un if et GOTO en dessous, et ils s'attendaient à ce que le else ;"
  • Avec le recul, il aurait fallu l'appeler "no break" (ou éventuellement "nobreak"), ce qui n'aurait pas prêté à confusion.

Donc, si la question est, "Pourquoi ne changent-ils pas ce mot-clé ?" alors Cat Plus Plus a probablement donné la réponse la plus précise - à ce stade, ce serait trop destructeur pour le code existant pour être pratique. Mais si la question que vous vous posez vraiment est pourquoi else a été réutilisé en premier lieu, eh bien, apparemment ça semblait être une bonne idée à l'époque.

Personnellement, j'aime le compromis consistant à commenter # no break en ligne partout où le else pourrait être confondu, à première vue, comme appartenant à la boucle. C'est raisonnablement clair et concis. Cette option est brièvement mentionnée dans le résumé que Bjorn a lié à la fin de sa réponse :

Pour être complet, je dois mentionner qu'avec un léger changement de syntaxe, les programmeurs qui veulent cette syntaxe peuvent l'avoir dès maintenant. les programmeurs qui veulent cette syntaxe peuvent l'avoir dès maintenant :

for item in sequence:
    process(item)
else:  # no break
    suite

* Citation bonus de cette partie de la vidéo : "Tout comme si nous appelions lambda <em>makefunction, </em>personne ne demandait : "Que fait Lambda ?".

2 votes

Pourquoi ne pas ajouter le support de nobreak à côté de else, faire en sorte que les deux soient égaux et existent l'un à côté de l'autre et faire une règle claire de type PEP que nobreak doit être utilisé à la place de else ?

2 votes

@jaaq Je ne peux pas parler pour les développeurs du noyau de Python mais considérez la ligne PEP 20 "Il devrait y avoir une - et de préférence une seule - manière évidente de le faire".

1 votes

Oui, c'est vrai, mais ils ont fait la même chose avec l'opérateur de division, où l'on pouvait importer la division de __future__ pour remplacer / par la division standard et ajouter l'opérateur de division d'étage //.

37voto

Cat Plus Plus Points 53385

Parce qu'ils ne voulaient pas introduire un nouveau mot-clé dans la langue. Chacun vole un identifiant et pose des problèmes de rétrocompatibilité, c'est donc généralement le dernier recours.

5 votes

On dirait que finally aurait été un meilleur choix dans ce cas. Le mot-clé finally n'était-il pas encore présent au moment où cette construction a été introduite ?

34 votes

@Wallacoloo finally n'est pas beaucoup mieux, parce qu'il implique que le bloc serait toujours exécuté après la boucle, et il ne l'est pas (parce que ce serait redondant de mettre le code à exécuter après la boucle).

0 votes

Il ne peut pas non plus être finally car la clause else est également exécutée lorsque continue est utilisé dans la boucle for -- c'est-à-dire éventuellement plusieurs fois et pas seulement à la fin.

14voto

pcalcao Points 10302

Je l'ai lu quelque chose comme :

Si les conditions pour exécuter la boucle sont toujours réunies, faites quelque chose, sinon faire autre chose.

0 votes

Votre toujours sur les conditions est utile (+1) bien qu'elle soit fausse - c'est humain ;-)

1 votes

-1 ; cette prononciation de for: / else: donne l'impression que le else: sera toujours exécuté après la boucle, ce qui n'est pas le cas.

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