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
ne se termine pas, ce qui est le contraire de ce qu'il fait! Quelle que soit ma façon de penser, mon cerveau ne peut pas passer de l'instruction for
au bloc else
de manière transparente. continue
ou continuewith
aurait plus de sens (et j'essaie de m'entraîner à le lire comme tel).
Je me demande comment les codeurs Python ont lu cette construction dans leur tête (ou à voix haute, si vous préférez). Peut-être me manque quelque chose qui rendrait de tels blocs de code plus facilement déchiffrables?
C'est une construction étrange même pour les codeurs aguerris de Python. Utilisé en conjonction avec des boucles for, cela signifie fondamentalement "trouve un élément dans l'itérable, sinon si rien n'a été trouvé ...". Un péché:
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 consiste à 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 aux deux autres versions, mais fonctionne assez bien dans du code critique non performant, qu’il importe ou pas d’itérer la liste entière. D'autres peuvent ne pas être d'accord, mais personnellement, je ne voudrais jamais utiliser les blocs for-else ou while-else dans le code de production.
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 que 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 de faire un deuxième test pour voir comment la boucle a été sortie.
Par exemple, supposons que je doive parcourir 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 de drapeau est manquant, une exception doit être déclenchée.
En utilisant la construction for
...else
de Python que vous avez
for i in mylist:
if i == theflag:
break
process(i)
else:
raise ValueError("List argument missing terminal flag.")
Comparez ceci à 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, la variable raise
est étroitement liée à la boucle for avec laquelle elle fonctionne. Dans le second cas, la liaison n’est pas aussi forte et des erreurs peuvent être introduites pendant la maintenance.
Une excellente présentation de Raymond Hettinger, intitulée Transforming Code in Beautiful, Idiomatic Python , dans laquelle il aborde brièvement l’histoire de la construction for ... else
. La section correspondante est "Distinguer plusieurs points de sortie dans des boucles" commençant à 15h50 et se poursuivant pendant environ trois minutes. Voici les points forts:
for ... else
en remplacement de certains cas d'utilisation GOTO
;else
était logique car "c'est ce que Knuth a utilisé et les gens savaient qu'à ce moment-là, toutes les [déclarations for
] avaient incorporé un if
et un GOTO
dessous, et ils s'attendaient à un else
;"Donc, si la question est: "Pourquoi ne changent-ils pas ce mot-clé?" then Cat Plus Plus a probablement donné la réponse la plus précise - à ce stade, il serait trop destructeur pour le code existant pour être pratique. Mais si la question que vous vous posez réellement est de savoir pourquoi else
a été réutilisé, eh bien, apparemment, cela semblait être une bonne idée à l'époque.
Personnellement, j'aime bien le fait de commenter # no break
en ligne chaque fois que la variable else
peut être confondue, en un coup d'œil, avec celle qui appartient à 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 devrais mentionner cela avec un léger changement dans syntaxe, les programmeurs qui veulent cette syntaxe peuvent l’avoir maintenant:
for item in sequence: process(item) else: # no break suite
* Citation bonus de cette partie de la vidéo: "Comme si nous appelions lambda makefunction, personne ne demanderait" Que fait lambda? "
Parce qu'ils ne voulaient pas introduire un nouveau mot clé dans la langue. Chacun vole un identifiant et provoque des problèmes de compatibilité ascendante, il s’agit donc généralement d’un dernier recours.
Le moyen le plus simple que j'ai trouvé pour «obtenir» ce que le/pour a fait et, plus important encore, quand l'utiliser, était de se concentrer sur l'endroit où la déclaration de rupture saute à. La construction For/else est un seul bloc. La pause saute hors du bloc et saute donc «par-dessus» la clause else. Si le contenu de la clause else suivait simplement la clause for, il ne serait jamais sauté de dessus, et la logique équivalente devrait donc être fournie en la plaçant dans un if. Cela a déjà été dit, mais pas tout à fait comme cela, cela pourrait aider quelqu'un d'autre. Essayez d'exécuter le fragment de code suivant. Je suis de tout coeur en faveur du commentaire «no break» 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')
Je l'ai lu quelque chose comme:
Si toujours sur les conditions pour exécuter la boucle, faire des choses, sinon faire autre chose.
Je pense que la documentation a une excellente explication de else, continue
[...] il est exécuté lorsque la boucle se termine par l'épuisement de la liste (avec for) ou lorsque la condition devient fausse (avec while), mais pas lorsque la boucle est terminée par une instruction break. "
Pour faire simple, vous pouvez le penser comme ça;
break
dans la boucle for
, la partie else
ne sera pas appelée.break
dans la boucle for
, la partie else
sera appelée.En d'autres termes, si l'itération de boucle for n'est pas "rompue" avec break
, la partie else
sera appelée.
C'est si simple.
Puisque la partie technique a été assez bien répondue, mon commentaire est juste en relation avec le confusion qui produit ce mot clé Recyclé.
Étant un langage de programmation très éloquent, l'utilisation abusive 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 cela, (sinon) faites-le". C'est impliqué dans notre propre langue.
Au lieu de cela, l’utilisation de ce mot clé avec les instructions while
et for
crée la confusion. La raison pour laquelle notre carrière de programmeurs nous a appris que la déclaration else
réside dans un arbre de décision; sa portée logique , un wrapper qui conditionally renvoie un chemin à suivre. Pendant ce temps, les instructions de boucle ont un objectif explicite figuratif d'atteindre quelque chose. L'objectif est atteint après des itérations continues d'un processus.
if / else
indique un chemin à suivre. Les boucles suivent un chemin jusqu’à ce que "l’objectif" soit terminé.
Le problème est que else
est un mot qui définit clairement la dernière option d'une condition. Les sémantiques du mot sont tous deux partagés par Python et le langage humain. Mais l’autre mot du langage humain n’est jamais utilisé pour indiquer les actions que quelqu'un ou quelque chose entreprendra une fois que quelque chose est terminé. Il sera utilisé si, au cours de son achèvement, un problème se pose (plutôt comme une déclaration break).
A la fin, le mot clé restera en Python. Il est clair que c’était une erreur, plus claire lorsque chaque programmeur essaie de créer une histoire pour en comprendre l’utilisation, comme un dispositif mnémonique. J'aurais aimé s'ils choisissaient plutôt le mot clé then
. Je crois que ce mot-clé s’intègre parfaitement dans ce flux itératif, le payoff après la boucle.
Cela ressemble à la situation que certains enfants ont après avoir suivi chaque étape du montage d'un jouet: EtALORSquel papa?
Je l'ai lu ainsi: "Lorsque la iterable
est complètement épuisée et que l'exécution est sur le point de passer à l'instruction suivante après avoir terminé la for
, la clause else sera exécutée." Ainsi, lorsque l'itération est interrompue par break
, cela ne sera pas exécuté.
Je suis d'accord, cela ressemble plus à un 'Elif pas [condition (s) levant la pause]'.
Je sais que c'est un vieux fil, mais je suis en train de regarder la même question en ce moment, et je ne suis pas sûr que quiconque ait capturé la réponse à cette question telle que je la comprends.
Pour moi, il y a trois façons de "lire" la else
dans les instructions For... else
ou While... else
, qui sont toutes équivalentes, sont les suivantes:
else
==
if the loop completes normally (without a break or error)
else
==
if the loop does not encounter a break
else
==
else not (condition raising break)
(vraisemblablement, une telle condition existe, sinon vous n'auriez pas de boucle)Donc, essentiellement, le "else" dans une boucle est vraiment un "Elif ..." où '...' est (1) pas de pause, ce qui équivaut à (2) PAS [condition (s) levant la pause ].
Je pense que la clé est que la else
est inutile sans la "pause", 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 plus simplement en anglais comme suit:
for:
do stuff
condition:
break
else: # read as "else not break" or "else not condition"
do more stuff
Comme l'ont dit les autres affiches, une pause est généralement levée lorsque vous êtes en mesure de localiser ce que votre boucle recherche, ainsi le else:
devient "que faire si l'élément cible n'est pas localisé".
Exemple
Vous pouvez également utiliser la gestion des exceptions, les pauses et les boucles for 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 pause 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 pause, pas de condition donnant lieu à une pause et aucune erreur n'est rencontrée.
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
----------
Les codes du bloc d'instructions else
seront exécutés lorsque la boucle for
ne serait pas cassée.
for x in xrange(1,5):
if x == 5:
print 'find 5'
break
else:
print 'can not find 5!'
#can not find 5!
À partir de docs: Interrompre et continuer les déclarations, ou encore Clauses on Loops
Les instructions de boucle peuvent avoir une clause else; il est exécuté lorsque la boucle se termine par l'épuisement de la liste (avec for) ou lorsque la condition devient fausse (avec while), mais pas lorsque la boucle est terminée par une instruction break. 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 bien: la clause else appartient à la boucle for, pas à l'instruction if.)
Lorsqu'elle est utilisée avec une boucle, la clause else a plus de points en commun avec la clause else d'une instruction try que celle des instructions if: la clause else d'une instruction try s'exécute lorsqu'aucune exception ne se produit, et la clause else d'une boucle s'exécute sans interruption . Pour plus d'informations sur l'instruction try et les exceptions, voir Gestion des exceptions.
L'instruction continue, également empruntée à C, continue à 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
Le mot clé else
peut être déroutant ici, et comme beaucoup de personnes l’ont souligné, un élément comme nobreak
, notbreak
est plus approprié.
Pour comprendre logiquement for ... else ...
, comparez-le avec try...except...else
et non pas if...else...
, la plupart des programmeurs python connaissent bien 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, considérez break
comme un type spécial 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 fastidieuse, mais elle clarifie la confusion.
Vous pourriez penser à cela, comme else
comme dans le reste des choses, ou les autres choses, qui n'ont pas été faites dans la boucle.
Voici une façon de penser à ce sujet que je n'ai vu personne mentionner plus haut:
Tout d’abord, rappelez-vous que les boucles for-loops ne sont fondamentalement que du sucre syntaxique. Par exemple, la boucle
for item in sequence:
do_something(item)
peut être réécrit (approximativement) comme
item = None
while sequence.hasnext():
item = sequence.next()
do_something(item)
Deuxièmement, rappelez-vous que les boucles while sont simplement répétées si des blocs! Vous pouvez toujours lire une boucle while comme "si cette condition est vraie, exécutez le corps, puis revenez et vérifiez à nouveau".
Ainsi, while/else est parfaitement logique: c'est exactement la même structure que if/else, avec la fonctionnalité supplémentaire de bouclage jusqu'à ce que la condition devienne fausse au lieu de simplement vérifier la condition une fois.
Et puis pour/else est tout à fait logique aussi: parce que toutes les boucles for-loop ne sont que du sucre syntaxique au-dessus de while-loops, il vous suffit de déterminer quel est le conditionnel implicite sous-jacent de while-boucle, et le reste correspond à la condition devient False.
En plus de la recherche, voici un autre cas d'utilisation idiomatique. Supposons que vous souhaitiez attendre qu'une condition soit vraie, par exemple. un port à ouvrir sur un serveur distant, avec un délai d'attente. Ensuite, vous pouvez utiliser une construction while...else
comme ceci:
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()
Python utilise une autre boucle après et tant, de sorte que si rien ne s'applique à la boucle, quelque chose d'autre se produit. Par exemple:
test = 3
while test == 4:
print("Hello")
else:
print("Hi")
La sortie serait "Salut" maintes et maintes fois (si je suis correct).
Supposons que nous ayons une fonction
def broken(x) : return False if x==5 else True
Ce qui signifie que seulement 5 n'est pas cassé. Maintenant dans le cas où broken 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ù quand broken est évalué avec 5: -
for x in range(6):
if not broken(x) : break
else:
print("Everything broken... Doom is upon us")
Cela n'imprimera rien. Ainsi, dire indirectement qu'il y a au moins quelque chose qui n'est pas cassé.
Cependant, au cas où vous voudriez tricher et ignorer quelque chose que vous avez trouvé était cassé. C'est-à-dire, continuez la boucle même si vous avez trouvé 5 comme cassé, sinon l'instruction sera toujours imprimée. C'est :-
for x in range(6):
if not broken(x) : continue
else:
print("Everything broken... Doom is upon us")
Va imprimer
Everything broken... Doom is upon us
J'espère que cela dissipe la confusion au lieu d'en créer un nouveau :-)
J'essayais juste de redonner un sens à moi-même. J'ai trouvé que ce qui suit aide!
• Pensez à la else
comme étant associée àif
à l'intérieur de la boucle (au lieu de la for
) - si la condition est remplie, cassez la boucle, sinon faites ceci - sauf qu'il s'agit d'une else
associée à plusieurs if
s!
• Si aucune if
s n'a été satisfaite du tout, faites la else
.
• Les multiples if
s peuvent également être considérés comme if
-Elif
s!
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 incroyablement simple, il suffit de dire
1, "si for clause
est terminé"
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 maniable d'écrire des instructions aussi longues que "for clause is complete", 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, il est complètement énoncé est la combinaison logique:
if "for clause is completed" or "not run at all":
do else stuff
ou le dire de cette façon:
if "for clause is not partially run":
do else stuff
ou de cette façon:
if "for clause not encounter a break":
do else stuff
Les bonnes réponses sont:
Ma note ici vient de ce que Donald Knuth a dit une fois (pardon, impossible de trouver une référence) qu'il existe une construction où while-else est indiscernable de if-else, à savoir (en Python):
x = 2
while x > 3:
print("foo")
break
else:
print("boo")
a le même flux (à l'exclusion des différences de niveau bas) que:
x = 2
if x > 3:
print("foo")
else:
print("boo")
Le fait est que if-else peut être considéré comme un sucre syntaxique pour while-else qui présente une rupture implicite à la fin de son bloc if
. L'implication contraire, cette boucle while
est l'extension de if
, car il s'agit simplement d'une vérification conditionnelle répétée. Cependant, il est insuffisant lorsque vous considérez si-else, car cela signifierait que else
block in while-else serait exécuté chaque fois lorsque la condition est fausse.
Pour faciliter votre compréhension, pensez de cette façon:
Sans la boucle
break
,return
, etc., la boucle ne se termine que lorsque la condition n'est plus vraie (dans le casfor
, vous devez considérer les bouclesfor
de style C ou les traduire enwhile
) et le blocelse
lorsque la condition est false.
Une autre note:
Une boucle intérieure prématurée
break
,return
, etc. empêche la condition de devenir fausse car l'exécution saute hors de la boucle alors que la condition était vraie et ne reviendrait jamais pour la vérifier à nouveau.