Pourquoi python utilisation 'd'autre' 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, car il suggère que le code en question ne fonctionne que si le bloc for n'est pas complet, ce qui est le contraire de ce qu'il fait! Peu importe ce que j'en pense, mon cerveau ne peut pas progresser de façon transparente de la déclaration for au bloc else . Pour moi, continue ou continuewith aurait plus de sens (et je suis en essayant de m'entraîner à le lire comme tel).

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

295
demandé sur Wolf 2012-04-02 20:18:45

17 réponses

c'est une construction étrange même pour les codeurs Python assaisonnés. Lorsqu'il est utilisé conjointement avec des boucles signifie "trouver un élément de l'itératif, d'autre si aucune n'a été trouvé ...". Comme dans:

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

Mais quand 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.')

Il n'est pas sémantiquement équivalent pour les deux autres versions, mais fonctionne assez bien dans le code critique Non-performance où il n'a pas d'importance que vous itériez la liste entière ou non. D'autres peuvent être en désaccord, mais personnellement j'éviterais d'utiliser les blocs for-else ou while-else dans le code de production.

Voir aussi [Python-idées] Résumé de pour...autres fils

181
répondu Björn Lindqvist 2018-09-19 15:22:37

une construction commune est d'exécuter une boucle jusqu'à ce que quelque chose soit trouvé et puis de 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 drapeau ou une variable de stockage qui me permettra de faire un deuxième test pour voir comment la boucle a été quitté.

par exemple, supposons que je dois chercher dans une liste et traiter chaque élément jusqu'à ce qu'un élément de drapeau soit trouvé et puis arrêter le traitement. Si l' l'élément de drapeau est manquant puis une exception doit être soulevée.

utilisant le Python for ... else construisez 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é à la boucle for avec laquelle il travaille. Dans la seconde, la liaison n'est pas aussi forte et des erreurs peuvent être introduites lors de l'entretien.

356
répondu Lance Helsten 2017-06-23 05:50:37

il y a une excellente présentation de Raymond Hettinger, intitulée Transforming Code into Beautiful, Idiomatic Python , dans laquelle il aborde brièvement l'histoire de la construction for ... else . La section pertinente est "distinction multiple exit points in loops " commençant à 15:50 et continuant pendant environ trois minutes. Voici les hauts points:

  • le for ... else construction a été conçu par Donald Knuth en remplacement de certains GOTO cas d'utilisation;
  • réutiliser le mot-clé else avait du sens parce que "C'est ce que Knuth utilisé, et les gens savaient, à ce moment-là, tous [ for déclarations] avait intégré un if et GOTO en dessous, et ils s'attendaient à la else ; "
  • en rétrospective, il aurait dû être appelé "no break" (ou peut-être "nobreak"), et alors il ne serait pas déroutant.*

donc, si la question est, " pourquoi ne changent-ils pas ce mot clé?"puis Chat Plus Plus probablement donné la réponse la plus précise – à ce point, il serait trop destructeur pour code existant pour être pratique. Mais si la question que vous posez vraiment est pourquoi else a été réutilisé en premier lieu, Eh bien, apparemment cela semblait être une bonne idée à l'époque.

Personnellement, j'aime le compromis de commentant # no break en ligne partout où le else pourrait être confondu, à un coup d'oeil, comme appartenant à l'intérieur de la boucle. C'est assez clair et concis. Cette option obtient une brève mention dans le résumé que Bjorn lié à la fin de sa réponse:

pour être complet, je dois mentionner qu'avec un léger changement dans syntaxe, 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 avons appelé lambda makefunction, personne ne demanderait, " que fait lambda?"

116
répondu Air 2017-05-23 12:03:08

parce qu'ils ne voulaient pas introduire un nouveau mot-clé à la langue. Chacun vole un identifiant et cause des problèmes de compatibilité à l'envers, donc c'est généralement le dernier recours.

29
répondu Cat Plus Plus 2012-04-02 16:20:03

le moyen le plus facile que j'ai trouvé pour "obtenir" ce que le for/else a fait, et plus important encore, quand l'utiliser, était de se concentrer sur l'endroit où la déclaration de pause saute à. La construction For / else est un bloc unique. Le break sort du bloc, et saute donc sur la clause "else". Si le contenu de la clause else suivait simplement la clause for, elle ne serait jamais dépassée, et donc la logique équivalente devrait être fournie en la mettant dans un si. Cela a déjà été dit, mais pas tout à fait dans ces mots, de sorte qu'il peut aider quelqu'un d'autre. Essayez d'exécuter le fragment de code suivant. Je suis tout à fait en faveur du commentaire "Pas de pause" pour plus de clarté.

for a in range(3):
    print(a)
    if a==4: # change value to force break or not
        break
else: #no break  +10 for whoever thought of this decoration
    print('for completed OK')

print('statement after for loop')
13
répondu Neil_UK 2015-02-05 12:54:02

Je l'ai lu quelque chose comme:

Si encore sur les conditions d'exécution de la boucle, faire des choses, else faire quelque chose d'autre.

12
répondu pcalcao 2012-04-02 16:20:33

je pense que la documentation a une grande explication de else , continue

[...] il est exécuté lorsque la boucle se termine par épuisement de la liste (avec for) ou lorsque la condition devient false (avec while), mais pas lorsque la boucle est terminée par une instruction break."

Source: Python 2 docs: Tutoriel sur les flux de contrôle

9
répondu Ayan 2016-04-25 11:38:56

pour faire simple, vous pouvez penser à lui comme cela;

  • si elle rencontre la commande break dans la boucle for , la partie else ne sera pas appelée.
  • si elle ne rencontre pas la commande break dans la boucle for , la partie else sera appelée.

en d'autres termes, si pour l'itération de boucle n'est pas "cassé" avec break , le else la partie sera appelée.

c'est si simple.

7
répondu Ad Infinitum 2018-06-26 08:42:33

puisque la partie technique a été pratiquement répondu, mon commentaire est juste en relation avec le confusion qui produisent ce recyclé mot-clé.

étant Python un très éloquent langage de programmation, l'usage abusif d'un mot-clé est plus notoire. Le mot-clé else décrit parfaitement une partie du flux d'un arbre de décision,"si vous ne pouvez pas faire ceci, (sinon) faites cela". C'est implicite dans notre propre langue.

à la place, l'utilisation de ce mot-clé avec les énoncés while et for crée de la confusion. La raison, notre carrière en tant que programmeurs nous a appris que la déclaration else réside dans un arbre de décision; son portée logique , un wrapper que conditionnellement retourner un chemin à suivre. En attendant, les déclarations de boucle ont un but figuratif explicite à atteindre quelque. Le but est atteint après une itération d'un processus.

if / else indiquer un chemin pour suivre . Boucles suivre un chemin jusqu'à ce que le" but "soit terminé .

Le problème est que else est un mot qui définissent clairement la dernière option dans une condition. Les sémantiques du mot sont à la fois partagés par Python et le langage humain. Mais l'autre mot dans le langage humain n'est jamais utilisé pour indiquer les actions que quelqu'un ou quelque chose va prendre après que quelque chose est terminé. Il sera utilisé si, dans le processus de la terminer, une question s'élève (plus comme un pause déclaration).

À la fin, le mot-clé restera en Python. Il est clair que c'était une erreur, plus clair quand chaque programmeur essaye de trouver une histoire pour comprendre son utilisation comme un dispositif mnémotechnique. J'aurais aimé, si ils ont choisi à la place le mot-clé then . Je crois que ce mot-clé s'inscrit parfaitement dans ce flux itératif, le payoff après la boucle.

il ressemble à cette situation qu'un enfant a après avoir suivi chaque étape dans l'assemblage d'un jouet: et puis quel Papa?

6
répondu 3rdWorldCitizen 2016-01-12 18:12:41

Je l'ai lu comme "quand le iterable est épuisé complètement, et l'exécution est sur le point de procéder à la déclaration suivante après avoir terminé le for , la clause else sera exécutée."Ainsi, lorsque l'itération est brisée par break , cela ne sera pas exécuté.

5
répondu 0xc0de 2014-07-07 04:32:57

je suis d'accord, c'est plus comme un " elif [condition(s) la collecte de pause]'.

je sais que c'est un vieux fil, mais je regarde dans la même question en ce moment, et je ne suis pas sûr que quelqu'un a capturé la réponse à cette question dans la façon dont je la comprends.

pour moi, il y a trois façons de "lire" les énoncés else dans For... else ou While... else , qui sont tous équivalents, sont:

  1. else == if the loop completes normally (without a break or error)
  2. else == if the loop does not encounter a break
  3. else == else not (condition raising break) (probablement il y a une telle condition, ou vous n'auriez pas une boucle)

donc, essentiellement, le "else" dans une boucle est vraiment un " elif ..." où.' .."est (1) Pas de pause, ce qui est équivalent à(2) pas [condition (s) pause levée].

je pense que la clé est que le else est inutile sans le "break", donc un for...else comprend:

for:
    do stuff
    conditional break # implied by else
else not break:
    do more stuff

ainsi, les éléments essentiels d'une boucle for...else sont les suivants, et vous les liriez dans un anglais plus simple comme:

for:
    do stuff
    condition:
        break
else: # read as "else not break" or "else not condition"
    do more stuff

comme les autres affiches ont dit, une pause est généralement soulevée quand vous êtes en mesure de localiser ce que votre boucle cherche, de sorte que le else: devient "que faire si l'élément cible n'est pas situé".

exemple

vous pouvez également utiliser la manipulation d'exception, les pauses, et pour les boucles tous ensemble.

for x in range(0,3):
    print("x: {}".format(x))
    if x == 2:
        try:
            raise AssertionError("ASSERTION ERROR: x is {}".format(x))
        except:
            print(AssertionError("ASSERTION ERROR: x is {}".format(x)))
            break
else:
    print("X loop complete without error")

résultat

x: 0
x: 1
x: 2
ASSERTION ERROR: x is 2
----------
# loop not completed (hit break), so else didn't run

exemple

exemple Simple avec une coupure frappée.

for y in range(0,3):
    print("y: {}".format(y))
    if y == 2: # will be executed
        print("BREAK: y is {}\n----------".format(y))
        break
else: # not executed because break is hit
    print("y_loop completed without break----------\n")

Résultat

y: 0
y: 1
y: 2
BREAK: y is 2
----------
# loop not completed (hit break), so else didn't run

exemple

exemple Simple où il n'y a pas de rupture, aucune condition soulevant une rupture, et aucune erreur ne sont rencontrées.

for z in range(0,3):
     print("z: {}".format(z))
     if z == 4: # will not be executed
         print("BREAK: z is {}\n".format(y))
         break
     if z == 4: # will not be executed
         raise AssertionError("ASSERTION ERROR: x is {}".format(x))
else:
     print("z_loop complete without break or error\n----------\n")

résultat

z: 0
z: 1
z: 2
z_loop complete without break or error
----------
3
répondu NotAnAmbiTurner 2016-06-08 17:22:29

le mot-clé else peut prêter à confusion ici, et comme beaucoup de gens l'ont souligné, quelque chose comme nobreak , notbreak est plus approprié.

afin de comprendre for ... else ... logiquement, comparez-le avec try...except...else , pas if...else... , la plupart des programmeurs python sont familiers avec le code suivant:

try:
    do_something()
except:
    print("Error happened.") # The try block threw an exception
else:
    print("Everything is find.") # The try block does things just find.

de même, pensez à break comme une sorte spéciale de Exception :

for x in iterable:
    do_something(x)
except break:
    pass # Implied by Python's loop semantics
else:
    print('no break encountered')  # No break statement was encountered

la différence est python implique except break et vous ne pouvez pas l'écrire, il devient donc:

for x in iterable:
    do_something(x)
else:
    print('no break encountered')  # No break statement was encountered

Oui, je sais que cette comparaison peut être difficile et fatigante, mais elle clarifie la confusion.

3
répondu cizixs 2017-05-05 02:36:42
Les Codes

dans le bloc de déclaration else seront exécutés lorsque la boucle for n'a pas été rompue.

for x in xrange(1,5):
    if x == 5:
        print 'find 5'
        break
else:
    print 'can not find 5!'
#can not find 5!

De la docs: break " et " continue, et d'autre Clauses sur les Boucles

les déclarations de boucle peuvent avoir une clause else; elle est exécutée lorsque la boucle se termine par épuisement de la liste (avec for) ou lorsque la condition devient false (avec while), mais pas lorsque la boucle est terminé par un break statement. Ceci est illustré par la boucle suivante, qui recherche des nombres premiers:

>>> for n in range(2, 10):
...     for x in range(2, n):
...         if n % x == 0:
...             print(n, 'equals', x, '*', n//x)
...             break
...     else:
...         # loop fell through without finding a factor
...         print(n, 'is a prime number')
...
2 is a prime number
3 is a prime number
4 equals 2 * 2
5 is a prime number
6 equals 2 * 3
7 is a prime number
8 equals 2 * 4
9 equals 3 * 3

(Oui, c'est le code correct. Regardez attentivement: la clause else appartient à la boucle for, pas à la déclaration if.)

lorsqu'elle est utilisée avec une boucle, la clause else a plus en commun avec la clause else d'une instruction try qu'avec celle de if statements: la clause else d'une instruction try s'exécute lorsqu'aucune exception ne se produit, et une la clause else de loop s'exécute lorsqu'aucune rupture ne se produit. Pour en savoir plus sur la déclaration try et les exceptions, consultez la rubrique Gestion des Exceptions.

la déclaration continue, également empruntée à C, continue avec la prochaine itération de la boucle:

>>> for num in range(2, 10):
...     if num % 2 == 0:
...         print("Found an even number", num)
...         continue
...     print("Found a number", num)
Found an even number 2
Found a number 3
Found an even number 4
Found a number 5
Found an even number 6
Found a number 7
Found an even number 8
Found a number 9
3
répondu GoingMyWay 2018-02-16 12:23:27

vous pourriez penser à lui comme, else comme dans le reste des choses, ou les autres choses, qui n'ont pas été faites dans la boucle.

2
répondu jamylak 2012-04-02 16:25:26

voici un autre cas d'utilisation idiomatique à part la recherche. Disons que vous vouliez attendre qu'une condition soit vraie, par exemple qu'un port soit ouvert sur un serveur distant, avec un certain temps mort. Alors vous pourriez utiliser une while...else construire comme ainsi:

import socket
import time

sock = socket.socket()
timeout = time.time() + 15
while time.time() < timeout:
    if sock.connect_ex(('127.0.0.1', 80)) is 0:
        print('Port is open now!')
        break
    print('Still waiting...')
else:
    raise TimeoutError()
1
répondu Sudicode 2018-03-29 14:03:29

supposons que nous ayons une fonction

def broken(x) : return False if x==5 else True

ce qui signifie que seulement 5 ne sont pas cassés. Maintenant dans le cas cassé n'est jamais évalué avec 5: -

for x in range(4):
    if not broken(x) : break
else:
    print("Everything broken... doom is upon us")

donnera la sortie: -

Everything broken... doom is upon us

où lorsque cassé est évalué avec 5: -

for x in range(6):
    if not broken(x) : break
else:
    print("Everything broken... doom is upon us")

il n'imprimera rien. Donc dire indirectement qu'il y a au moins quelque chose ce qui n'est pas cassé.

cependant, au cas où vous voulez tricher et sauter quelque chose que vous avez trouvé était cassé. Qui est, continuer la boucle, même si vous avez trouvé 5 cassés, else sera toujours imprimé. C'est-à-dire: -

for x in range(6):
    if not broken(x) : continue
else:
    print("Everything broken... doom is upon us")

imprime

Everything broken... doom is upon us

j'espère qu'il efface la confusion au lieu d'en créer une nouvelle :-)

0
répondu satyakam shashwat 2018-03-23 10:54:01
for i in range(3):
    print(i)

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

"else" ici est follement simple, juste moyen

1, " si for clause est rempli "

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

    if i == 2:
        print("Too big - I'm giving up!")
        break;
if "for clause is completed":
    print("Completed successfully")

c'est agaçant d'écrire des énoncés aussi longs que" for clause is completed", donc ils introduisent"else".

else voici un si dans sa nature.

2, cependant, que diriez-vous de for clause is not run at all

In [331]: for i in range(0):
     ...:     print(i)
     ...: 
     ...:     if i == 9:
     ...:         print("Too big - I'm giving up!")
     ...:         break
     ...: else:
     ...:     print("Completed successfully")
     ...:     
Completed successfully

donc c'est complètement logique combinaison:

if "for clause is completed" or "not run at all":
     do else stuff

ou le mettre de cette façon:

if "for clause is not partially run":
    do else stuff

ou par ici:

if "for clause not encounter a break":
    do else stuff
0
répondu JawSaw 2018-08-25 04:29:46