Por que python usa 'else' depois de loops for e while?

481

Eu entendo como essa construção funciona:

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

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

Mas não entendo por que elseé usada como palavra-chave aqui, pois sugere que o código em questão só é executado se o forbloco não for concluído, o que é o oposto do que faz! Não importa como eu pense sobre isso, meu cérebro não pode progredir perfeitamente da fordeclaração para o elsebloco. Para mim, continueou continuewithfaria mais sentido (e estou tentando me treinar para lê-lo como tal).

Eu estou querendo saber como os codificadores Python lêem essa construção em suas cabeças (ou em voz alta, se você quiser). Talvez esteja faltando algo que tornaria esses blocos de código mais facilmente decifráveis?

Kent Boogaart
fonte
26
Você pode traduzir para "então" na sua cabeça.
Marcin
63
Não esqueça a linha-chave no Zen do Python: "... dessa maneira, pode não ser óbvio a princípio, a menos que você seja holandês".
22812 Daniel Roseman
51
Na minha cabeça, eu traduzo para "se não quebrar" . E, uma vez que breaké muito usada em "Eu encontrei ele" loops, você pode traduzi-lo para "se não for encontrado" , que não está longe do que else
MestreLion
29
Eu acho que a verdadeira pergunta que muitas pessoas têm aqui é "Qual é a diferença entre for ... else foo()e apenas colocar foo()depois do loop for?" E a resposta é que eles se comportam de maneira diferente apenas se o loop contiver um break(como descrito em detalhes abaixo).
Sam Kauffman
10
Um ponto e vírgula em python ... meus olhos doem .. mesmo que seja sintaticamente corrigir não é boa prática para fazê-lo
DarkCygnus

Respostas:

278

É uma construção estranha mesmo para codificadores Python experientes. Quando usado em conjunto com for-loops, significa basicamente "encontre algum item no iterável, caso contrário, se nenhum foi encontrado, faça ...". Como em:

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

Mas sempre que você vê essa construção, uma alternativa melhor é encapsular a pesquisa em uma função:

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

Ou use uma compreensão da lista:

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

Não é semanticamente equivalente às outras duas versões, mas funciona bem o suficiente no código crítico sem desempenho, onde não importa se você interage ou não a lista inteira. Outros podem discordar, mas eu pessoalmente evitaria usar os blocos for-else ou while-else no código de produção.

Veja também [Python-ideas] Resumo dos tópicos for ... else

Björn Lindqvist
fonte
50
A compreensão da lista é a única linha errada. Se você estiver procurando por um único item, como nos forexemplos de loop, e quiser usar uma expressão de gerador / compreensão de lista, deseje next((o for o in objects if o.key == search_key), None)ou envolva-o em try/ excepte não use nenhum valor padrão em vez de if/ else.
agf
4
e, como a resposta de Lance Helsten, há casos reais em que é melhor usar uma for/elseconstrução.
Andrean
5
Felicidades. Eu tinha um arquivo mal recuado onde elsefui emparelhado com um fore não fazia ideia de que era legal.
Maxhwb
3
Eu acho que o loop for é o mais óbvio dos construtos lá.
Route de milhas
14
Vale ressaltar que a cláusula else será executada mesmo se o loop for tiver valores, a menos que uma breakinstrução seja explicitamente executada como neste exemplo. Dos documentos acima: "A elsecláusula tem outro problema percebido: se não houver breakno loop, a elsecláusula é funcionalmente redundante.". por exemplofor x in [1, 2, 3]:\n print x\n else:\n print 'this executes due to no break'
dhackner 28/08
587

Uma construção comum é executar um loop até que algo seja encontrado e depois sair do loop. O problema é que, se eu sair do loop ou o loop terminar, preciso determinar qual caso aconteceu. Um método é criar um sinalizador ou armazenar uma variável que me permita fazer um segundo teste para ver como o loop foi encerrado.

Por exemplo, suponha que eu precise pesquisar em uma lista e processar cada item até que um item de sinalizador seja encontrado e pare o processamento. Se o item do sinalizador estiver ausente, será necessária uma exceção.

Usando a construção Python for... elsevocê tem

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

Compare isso com um método que não usa este açúcar sintático:

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

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

No primeiro caso, ele raiseestá vinculado firmemente ao loop for com o qual trabalha. No segundo, a ligação não é tão forte e erros podem ser introduzidos durante a manutenção.

Lance Helsten
fonte
69
Isso explica melhor do que a resposta escolhida, na qual o autor realmente não entende o que é o resto.
erikbwork
17
Eu diria que esse açúcar sintático pode apodrecer os dentes do seu projeto. Isso não faria um Python: the good partslivro.
boatcoder
1
Você pode confirmar que, no seu exemplo, isso process(i)acontece para todos os itens mylistestritamente antes theflage não para theflagsi mesmo? É o que foi planejado?
bli
4
processserá executado em cada um ique existe na lista antes de theflagser atingido, não será executado nos elementos da lista depois theflage não será executado theflag.
Lance Helsten
1
a instrução else também é executado se o iterable não tem elementos
Perdido Crotchet
173

Há uma excelente apresentação de Raymond Hettinger, intitulada Transforming Code into Beautiful, Python Idiomatic , na qual ele aborda brevemente a história da for ... elseconstrução. A seção relevante é "Distinguindo vários pontos de saída em loops", começando às 15:50 e continuando por cerca de três minutos. Aqui estão os pontos altos:

  • A for ... elseconstrução foi criada por Donald Knuth como um substituto para certos GOTOcasos de uso;
  • Reutilizar a elsepalavra - chave fazia sentido porque "era o que Knuth usava, e as pessoas sabiam, na época, que todas as [ fordeclarações] haviam incorporado um ife GOTOpor baixo, e esperavam o else;"
  • Em retrospectiva, deveria ter sido chamado de "sem interrupção" (ou possivelmente "sem interrupção") e, portanto, não seria confuso. *

Portanto, se a pergunta for: "Por que eles não alteram essa palavra-chave?" então o Cat Plus Plus provavelmente deu a resposta mais precisa - nesse momento, seria muito destrutivo para o código existente ser prático. Mas se a pergunta que você realmente está perguntando é por que elsefoi reutilizada em primeiro lugar, bem, aparentemente parecia uma boa ideia na época.

Pessoalmente, gosto do compromisso de comentar # no breakem linha onde quer que elsepossa ser confundido, de relance, como pertencendo ao loop. É razoavelmente claro e conciso. Esta opção recebe uma breve menção no resumo que Bjorn vinculou no final de sua resposta:

Para completar, devo mencionar que, com uma ligeira alteração na sintaxe, os programadores que desejam essa sintaxe podem tê-la agora:

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

* Citação de bônus dessa parte do vídeo: "Assim como se chamássemos lambda makefunction, ninguém perguntaria: 'O que o lambda faz?'"

Ar
fonte
33

Porque eles não queriam introduzir uma nova palavra-chave no idioma. Cada um rouba um identificador e causa problemas de compatibilidade com versões anteriores, por isso geralmente é o último recurso.

Cat Plus Plus
fonte
2
Parece que finallyteria sido uma escolha melhor nesse caso. A palavra-chave finalmente ainda não estava presente no momento em que essa construção foi introduzida?
Ponkadoodle
26
O @Wallacoloo finallynão é muito melhor, porque implica que o bloco sempre será executado após o loop, e não é (porque seria redundante apenas colocar o código para executar após o loop).
Cat Plus Plus
Também não pode ser finallyporque a cláusula else é executada também quando continueé usada no loop for - isso é possível muitas vezes e não apenas no final.
PEPR
6
@pepr elseexecução cláusula não é afetado por continue( docs e código de teste )
Air
@AirThomas: +1. Você está certo. O elseé executado apenas quando continuefoi o da última iteração.
pepr 16/05
33

Para simplificar, você pode pensar assim;

  • Se encontrar o breakcomando no forloop, a elseparte não será chamada.
  • Se não encontrar o breakcomando no forloop, a elseparte será chamada.

Em outras palavras, se a iteração for loop não for "interrompida" break, a elseparte será chamada.

Ao infinito
fonte
O elsebloco também não será executado se o corpo do loop gerar uma exceção.
Amal K
17

A maneira mais fácil de encontrar o que o for / else fez e, mais importante, quando usá-lo, foi me concentrar em onde a declaração de interrupção salta. A construção For / else é um único bloco. O intervalo salta para fora do bloco e, assim, salta 'sobre' a cláusula else. Se o conteúdo da cláusula else simplesmente seguisse a cláusula for, ela nunca seria ignorada e, portanto, a lógica equivalente teria que ser fornecida colocando-a em um if. Isso já foi dito antes, mas não exatamente nessas palavras, por isso pode ajudar outra pessoa. Tente executar o seguinte fragmento de código. Sou totalmente a favor do comentário "sem interrupção" para maior clareza.

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')
Neil_UK
fonte
"O intervalo salta para fora do bloco e, portanto, salta sobre a cláusula else" - embora isso possa ser útil como uma maneira de "obter" for:/ else:, ele realmente não fornece uma justificativa para a palavra-chave ser else. Dada a estrutura dada aqui, then:parece que seria muito mais natural. (Não são razões para elseser escolhido, dadas em outras respostas - eles apenas não está fornecidas aqui.)
Mark Amery
16

Eu acho que a documentação tem uma ótima explicação de mais , continue

[...] é executado quando o loop termina por exaustão da lista (com for) ou quando a condição se torna falsa (com while), mas não quando o loop é finalizado por uma instrução break. "

Fonte: Python 2 docs: Tutorial sobre controle de fluxo

Ayan
fonte
13

Eu li algo como:

Se ainda sobre as condições para executar o loop, fazer coisas, o mais fazer outra coisa.

pcalcao
fonte
Sua ainda nas condições é útil (+1), embora seja errado - é humano ;-)
Lobo
-1; essa pronúncia de for:/ else:faz parecer que o else:sempre será executado após o loop, o que não é o caso.
Mark Amery
11

Como a parte técnica foi praticamente respondida, meu comentário está relacionado à confusão que produz essa palavra-chave reciclada .

Sendo o Python uma linguagem de programação muito eloqüente , o uso indevido de uma palavra-chave é mais notório. A elsepalavra-chave descreve perfeitamente parte do fluxo de uma árvore de decisão, "se você não pode fazer isso, (mais) faz isso". Está implícito em nossa própria língua.

Em vez disso, o uso dessa palavra-chave com instruções whilee forcria confusão. O motivo, nossa carreira como programadores nos ensinou que a elsedeclaração reside em uma árvore de decisão; seu escopo lógico , um invólucro que retorna condicionalmente um caminho a seguir. Enquanto isso, as instruções de loop têm um objetivo explícito figurativo de alcançar algo. O objetivo é alcançado após iterações contínuas de um processo.

if / else indicar um caminho a seguir . Os loops seguem um caminho até que o "objetivo" seja concluído .

A questão é que elseé uma palavra que define claramente a última opção em uma condição. A semântica da palavra é compartilhada por Python e Human Language. Mas a palavra else em Human Language nunca é usada para indicar as ações que alguém ou alguma coisa executará após a conclusão de algo. Ele será usado se, no processo de conclusão, surgir um problema (mais como uma declaração de quebra ).

No final, a palavra-chave permanecerá em Python. É claro que foi um erro, mais claro quando todo programador tenta criar uma história para entender seu uso como um dispositivo mnemônico. Eu adoraria se eles tivessem escolhido a palavra-chave then. Acredito que essa palavra-chave se encaixa perfeitamente nesse fluxo iterativo, o retorno após o loop.

Assemelha-se à situação que uma criança tem depois de seguir todos os passos na montagem de um brinquedo: E ENTÃO o que papai?

3rdWorldCitizen
fonte
Eu acho que essa resposta aborda a questão da confusão sobre a qual o OP estava falando. A palavra-chave else faz exatamente o oposto do que você esperaria do significado em inglês de else quando anexado à ação de for. Em teoria, o for ... else poderia ter funcionado de maneira diferente, pois você acaba na parte else quando o loop é rompido, mas o problema é que, para usá-lo para encontrar o elemento x, e lidar com o caso em que x é não encontrado, você pode ter que usar uma bandeira ou outro teste após o todo para .. else construct #
Spacen Jasset 31/03
7

Eu li como "Quando o iterablearquivo estiver completamente esgotado, e a execução estiver prestes a prosseguir para a próxima instrução depois de terminar o for, a cláusula else será executada". Assim, quando a iteração é interrompida break, isso não será executado.

0xc0de
fonte
6

Eu concordo, é mais como um 'elif not (condição (s) que aumenta o intervalo]'.

Sei que esse é um tópico antigo, mas estou analisando a mesma pergunta agora e não tenho certeza de que alguém tenha capturado a resposta para essa pergunta da maneira que eu a entendo.

Para mim, existem três maneiras de "ler" as instruções elsein For... elseou While... else, todas equivalentes, são:

  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) (presumivelmente existe essa condição ou você não teria um loop)

Portanto, essencialmente, o "else" em um loop é realmente um "elif ..." onde '...' é (1) sem interrupção, o que equivale a (2) NOT [condição (s) que aumenta a interrupção].

Eu acho que a chave é que isso elseé inútil sem o 'intervalo', então a for...elseinclui:

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

Portanto, os elementos essenciais de um for...elseloop são os seguintes, e você os leria em inglês mais claro como:

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

Como os outros pôsteres disseram, geralmente ocorre uma interrupção quando você é capaz de localizar o que seu loop está procurando, else:tornando - se "o que fazer se o item de destino não estiver localizado".

Exemplo

Você também pode usar manipulação de exceção, quebras e loops todos juntos.

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")

Resultado

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

Exemplo

Exemplo simples com uma quebra sendo atingida.

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")

Resultado

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

Exemplo

Exemplo simples em que não há interrupção, nenhuma condição que gera uma interrupção e nenhum erro é encontrado.

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")

Resultado

z: 0
z: 1
z: 2
z_loop complete without break or error
----------
NotAnAmbiTurner
fonte
6

A elsepalavra-chave pode ser confusa aqui e, como muitas pessoas apontaram, algo como nobreak, notbreaké mais apropriado.

Para entender for ... else ...logicamente, compare-o com try...except...else, e não if...else..., a maioria dos programadores python está familiarizada com o seguinte código:

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.

Da mesma forma, pense breakcomo um tipo especial 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

A diferença está pythonimplícita except breake você não pode escrevê-la, então ela se torna:

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

Sim, eu sei que essa comparação pode ser difícil e cansativa, mas esclarece a confusão.

cizixs
fonte
Você deve criar um link para o recurso ao copiar: o Python Notes de Nick Coghlan .
Godaygo
@godaygo obrigado pelo link. Li e aceito o conceito quando aprendi python pela primeira vez, não memorizei a fonte ao escrever a resposta.
Cizixs
@cizixs Você "não memorizou a fonte", mas por acaso incluiu frases inteiras de comentários idênticos ao original? Ooookaaaay.
Mark Amery
5

Os códigos no elsebloco de instruções serão executados quando o forloop não for quebrado.

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

Dos documentos: quebrar e continuar instruções e outras cláusulas em loops

As instruções de loop podem ter uma cláusula else; é executado quando o loop termina por exaustão da lista (com for) ou quando a condição se torna falsa (com while), mas não quando o loop é finalizado por uma instrução break. Isso é exemplificado pelo seguinte loop, que procura por números primos:

>>> 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

(Sim, esse é o código correto. Observe atentamente: a cláusula else pertence ao loop for, não à instrução if.)

Quando usada com um loop, a cláusula else tem mais em comum com a cláusula else de uma instrução try do que com as declarações if: a cláusula else de uma instrução try é executada quando nenhuma exceção ocorre e a cláusula else de um loop é executada quando nenhuma interrupção ocorre . Para saber mais sobre a instrução try e as exceções, consulte Manipulando exceções.

A instrução continue, também emprestada de C, continua com a próxima iteração do loop:

>>> 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
Indo à minha maneira
fonte
1
Isso não acrescenta nada e não responde à pergunta, que não é como, mas por quê .
Air
5

Aqui está uma maneira de pensar sobre isso que eu não vi mais ninguém mencionar acima:

Primeiro, lembre-se de que os for-loops são basicamente apenas açúcar sintático em torno dos while-loops. Por exemplo, o loop

for item in sequence:
    do_something(item)

pode ser reescrito (aproximadamente) como

item = None
while sequence.hasnext():
    item = sequence.next()
    do_something(item)

Segundo, lembre-se de que os while-loops são basicamente apenas repetidos if-blocks! Você sempre pode ler um loop while como "se essa condição for verdadeira, execute o corpo e volte e verifique novamente".

Portanto, while / else faz todo sentido: é exatamente a mesma estrutura que if / else, com a funcionalidade adicional de loop até que a condição se torne falsa, em vez de apenas verificar a condição uma vez.

E então for / else também faz todo o sentido: como todos os loops for são apenas açúcar sintático em cima dos loops while, você só precisa descobrir qual é a condicionalidade implícita subjacente do loop while, e então o resto corresponde a quando condição torna-se falsa.

Aaron Gable
fonte
4

Ótimas respostas são:

  • isso que explica a história, e
  • isso fornece a citação correta para facilitar sua tradução / compreensão.

Minha observação aqui vem do que Donald Knuth disse uma vez (desculpe, não consigo encontrar referência) de que existe uma construção em que while-else é indistinguível de if-else, a saber (em Python):

x = 2
while x > 3:
    print("foo")
    break
else:
    print("boo")

tem o mesmo fluxo (excluindo diferenças de baixo nível) que:

x = 2
if x > 3:
    print("foo")
else:
    print("boo")

O ponto é que o if-else pode ser considerado como açúcar sintático por enquanto, o que está implícito breakno final de seu ifbloco. A implicação oposta, à qual esse whileloop é extensão if, é mais comum (é apenas uma verificação condicional repetida / em loop), porque ifgeralmente é ensinada antes while. No entanto, isso não é verdade, porque isso significaria um elsebloqueio enquanto outro seria executado sempre que a condição for falsa.

Para facilitar sua compreensão, pense dessa maneira:

Sem break, returnetc., o loop termina somente quando a condição não for mais verdadeira e, nesse caso, o elsebloco também será executado uma vez. No caso de Python, forvocê deve considerar forloops no estilo C (com condições) ou traduzi-los para while.

Outra nota:

Prematura break, returnetc. loop dentro torna impossível para a condição para se tornar falsa porque a execução saltou para fora do loop while condição era verdade e que nunca iria voltar a verificar-lo novamente.

WloHu
fonte
3

Você poderia pensar nisso como, elseno resto das coisas, ou nas outras coisas, que não foi feito no circuito.

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

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

"else" aqui é loucamente simples, apenas significa

1, "se for clausefor concluído"

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")

É empolgante escrever declarações longas como "para a cláusula for concluída", para que elas introduzam "else".

else aqui está um se em sua natureza.

2, no entanto, que tal 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

Portanto, é completamente declaração é combinação lógica:

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

ou coloque desta maneira:

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

ou desta maneira:

if "for clause not encounter a break":
    do else stuff
Cálculo
fonte
else atua como "transação" no SQL.
Cálculo
2

Aqui está outro caso de uso idiomático além da pesquisa. Digamos que você queira esperar que uma condição seja verdadeira, por exemplo, uma porta a ser aberta em um servidor remoto, juntamente com algum tempo limite. Então você pode utilizar uma while...elseconstrução como esta:

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()
Jonathan Sudiaman
fonte
1

Eu só estava tentando entender isso de novo. Eu achei que o seguinte ajuda!

• Pense no elsecomo sendo emparelhado com o ifinterior do loop (em vez de com o for) - se a condição for atendida, quebre o loop, caso contrário, faça isso - exceto que seja um elseemparelhado com vários ifs!
• Se nenhum ifs foi satisfeito, faça o else.
• Os múltiplos ifs também podem ser considerados como if- elifs!

Germaine Goh
fonte
-2

Considero a estrutura como (if) A else B, e for (if) -seo é um if-else especial , aproximadamente . Pode ajudar a entender mais .

A e B são executados no máximo uma vez, o que é a mesma estrutura if-else.

for (if) pode ser considerado como um if especial, que executa um loop para tentar atender à condição if. Quando a condição if for atendida, A e interrompa ; Else , B.

Jie Zhang
fonte
-2

O Python usa outros loops for e while para que, se nada se aplica ao loop, algo mais aconteça. Por exemplo:

test = 3
while test == 4:
     print("Hello")
else:
     print("Hi")

A saída seria 'Hi' repetidamente (se eu estiver correto).

Mrmongoose64
fonte