Try-else do Python

581

Qual é o uso pretendido da elsecláusula opcional da trydeclaração?

geowa4
fonte
1
A maioria das respostas parece se concentrar em por que não podemos simplesmente colocar o material na cláusula else na própria cláusula try. A pergunta stackoverflow.com/questions/3996329 pergunta especificamente por que o código da cláusula else não pode ir após o próprio bloco try, e essa pergunta foi copiada para esta, mas não vejo uma resposta clara a essa pergunta aqui. Sinto que stackoverflow.com/a/3996378/1503120 responde excelentemente a essa pergunta. Eu também tentei elucidar o significado das várias cláusulas em stackoverflow.com/a/22579805/1503120 .
22414 jamadagni
Você deseja que algo ocorra se a exceção não for acionada, antes da limpeza final, que nem sempre deve acionar a mesma manipulação de exceção.
benjimin 17/04

Respostas:

858

As instruções no elsebloco são executadas se a execução cair na parte inferior do try- se não houver exceção. Honestamente, nunca encontrei uma necessidade.

No entanto, o tratamento de exceções observa:

O uso da cláusula else é melhor do que adicionar código adicional à cláusula try, pois evita a captura acidental de uma exceção que não foi gerada pelo código que está sendo protegido pela instrução try ... exceto.

Portanto, se você possui um método que pode, por exemplo, lançar um IOErrore deseja capturar exceções, isso ocorre, mas há outra coisa que você deseja fazer se a primeira operação for bem-sucedida e não desejar capturar um IOError de nessa operação, você pode escrever algo como isto:

try:
    operation_that_can_throw_ioerror()
except IOError:
    handle_the_exception_somehow()
else:
    # we don't want to catch the IOError if it's raised
    another_operation_that_can_throw_ioerror()
finally:
    something_we_always_need_to_do()

Se você apenas colocar another_operation_that_can_throw_ioerror()depois operation_that_can_throw_ioerror, exceptele capturaria os erros da segunda chamada. E se você colocar depois de todo o trybloco, ele sempre será executado, e não até depois do finally. O elsepermite garantir

  1. a segunda operação é executada apenas se não houver exceção,
  2. é executado antes do finallybloco e
  3. qualquer IOErrors que levanta não é pego aqui
Blair Conrad
fonte
7
Também tenha em mente que as variáveis utilizadas no try-bloco pode ser usado no resto do bloco, então você deve considerar sempre usando esta variante se você não esperar mais excepções no resto do bloco
WorldSEnder
3
Isso não importa, porque as variáveis ​​com escopo de tentativa são vistas fora da tentativa, independentemente de haver outra coisa ou não.
Reinderien
36
Não existe uma variável com escopo definido por tentativa. No Python, os escopos variáveis ​​são estabelecidos apenas por módulos, funções e compreensões, e não estruturas de controle.
mhsmith
9
A cláusula else permite escrever código que só faz sentido se uma exceção não for lançada; a cláusula exceto pode simplesmente passar. Se você colocar a lógica no bloco try, corre o risco de ocultar silenciosamente os erros no seu código. Nunca esmague exceções que você não esperava.
perfil completo de Alice Purcell
9
não está claro nesta resposta o que significa "cair do fundo" - não apenas isso acontece por causa de uma exceção, mas também, por causa de a return, continueou break.
Antti Haapala 03/03
108

Há uma grande razão para usar else- estilo e legibilidade. Geralmente, é uma boa idéia manter o código que pode causar exceções perto do código que lida com eles. Por exemplo, compare estes:

try:
    from EasyDialogs import AskPassword
    # 20 other lines
    getpass = AskPassword
except ImportError:
    getpass = default_getpass

e

try:
    from EasyDialogs import AskPassword
except ImportError:
    getpass = default_getpass
else:
    # 20 other lines
    getpass = AskPassword

O segundo é bom quando exceptnão é possível retornar mais cedo ou repetir a exceção. Se possível, eu teria escrito:

try:
    from EasyDialogs import AskPassword
except ImportError:
    getpass = default_getpass
    return False  # or throw Exception('something more descriptive')

# 20 other lines
getpass = AskPassword

Nota: Resposta copiada da duplicata postada recentemente aqui , portanto, todo esse material "AskPassword".

Izkata
fonte
53

Um uso: teste algum código que deve gerar uma exceção.

try:
    this_should_raise_TypeError()
except TypeError:
    pass
except:
    assert False, "Raised the wrong exception type"
else:
    assert False, "Didn't raise any exception"

(Este código deve ser abstraído para um teste mais genérico na prática.)

Darius Bacon
fonte
50

Try-else do Python

Qual é o uso pretendido da elsecláusula opcional da instrução try?

O uso pretendido é ter um contexto para que mais código seja executado, se não houver exceções nas quais se espera que sejam manipuladas.

Esse contexto evita o tratamento acidental de erros que você não esperava.

Mas é importante compreender as condições precisas que causam a cláusula else a prazo, porque return, continuee breakpode interromper o fluxo de controle para else.

Em suma

A elsedeclaração é executado se houver exceções e se não for interrompida por um return, continueou breakdeclaração.

As outras respostas perdem essa última parte.

Dos documentos:

A elsecláusula opcional é executada se e quando o controle fluir no final da trycláusula. *

(Negrito adicionado.) E a nota de rodapé diz:

* Atualmente, o controle “flui para fora da extremidade”, exceto no caso de uma exceção ou a execução de um return, continueou breakdeclaração.

Requer pelo menos uma cláusula exceto anterior ( consulte a gramática ). Portanto, realmente não é "try-else", é "try-except-else (-finally)", com o else(e finally) sendo opcional.

O Tutorial do Python detalha o uso pretendido:

A instrução try ... except possui uma cláusula opcional else, que, quando presente, deve seguir todas as exceções. É útil para o código que deve ser executado se a cláusula try não gerar uma exceção. Por exemplo:

for arg in sys.argv[1:]:
    try:
        f = open(arg, 'r')
    except IOError:
        print 'cannot open', arg
    else:
        print arg, 'has', len(f.readlines()), 'lines'
        f.close()

O uso da cláusula else é melhor do que adicionar código adicional à cláusula try, pois evita a captura acidental de uma exceção que não foi gerada pelo código que está sendo protegido pela instrução try ... exceto.

Exemplo de diferenciação elseversus código após o trybloco

Se você manipular um erro, o elsebloco não será executado. Por exemplo:

def handle_error():
    try:
        raise RuntimeError('oops!')
    except RuntimeError as error:
        print('handled a RuntimeError, no big deal.')
    else:
        print('if this prints, we had no error!') # won't print!
    print('And now we have left the try block!')  # will print!

E agora,

>>> handle_error()
handled a RuntimeError, no big deal.
And now we have left the try block!
Aaron Hall
fonte
26

Try-except-else é ótimo para combinar o padrão EAFP com a digitação de pato :

try:
  cs = x.cleanupSet
except AttributeError:
  pass
else:
  for v in cs:
    v.cleanup()

Você pode achar que esse código ingênuo é bom:

try:
  for v in x.cleanupSet:
    v.clenaup()
except AttributeError:
  pass

Essa é uma ótima maneira de ocultar acidentalmente erros graves no seu código. Digitei a limpeza lá, mas o AttributeError que me informaria está sendo engolido. Pior, e se eu o tivesse escrito corretamente, mas ocasionalmente o método de limpeza recebia um tipo de usuário com um atributo com nome errado, causando falhas silenciosas no meio do caminho e deixando um arquivo não fechado? Boa sorte na depuração dessa.

Alice Purcell
fonte
19

Acho que é realmente útil quando você precisa fazer a limpeza, mesmo que haja uma exceção:

try:
    data = something_that_can_go_wrong()
except Exception as e: # yes, I know that's a bad way to do it...
    handle_exception(e)
else:
    do_stuff(data)
finally:
    clean_up()
RoadieRich
fonte
9

Mesmo que você não consiga pensar em usá-lo agora, pode apostar que deve haver um uso para ele. Aqui está uma amostra sem imaginação:

Com else:

a = [1,2,3]
try:
    something = a[2]
except:
    print "out of bounds"
else:
    print something

Sem else:

try:
    something = a[2]
except:
    print "out of bounds"

if "something" in locals():
    print something

Aqui você tem a variável somethingdefinida se nenhum erro for gerado. Você pode remover isso fora do trybloco, mas isso requer alguma detecção confusa se uma variável for definida.

Desconhecido
fonte
3
O que há de errado com something = a[2]; print somethingdentro do bloco try:?
1350 S.Lott
@ S.Lott nada, mas e se alguém lhe enviar uma lista e você não quiser exibir os dados se não forem longos o suficiente porque provavelmente estão corrompidos?
Desconhecido
12
S. Lott: 'imprimir algo' pode gerar uma exceção diferente que você não deseja interceptar.
Darius Bacon
Eu não vejo a diferença. Se eu receber uma exceção fora dos limites, ela imprimirá "fora dos limites". Percebido. Se eu receber outra exceção, ela não é capturada por esse bloco de código. Se não houver exceção, o comportamento é imprimir o valor de algo, que é um [2]. Não vejo o que os outros fazem neste exemplo.
1313 St.Lott
3
O valor de 'alguma coisa', quando impresso, pode gerar o erro em seu método __str __ (). Embora esse valor seja realmente apenas 2 neste exemplo, você também pode indicar que não há nenhuma exceção fora dos limites aqui.
Darius Bacon
8

Há um bom exemplo disso try-elseno PEP 380 . Basicamente, tudo se resume a manipular exceções diferentes em diferentes partes do algoritmo.

É algo como isto:

try:
    do_init_stuff()
except:
    handle_init_suff_execption()
else:
    try:
        do_middle_stuff()
    except:
        handle_middle_stuff_exception()

Isso permite que você escreva o código de manipulação de exceção mais próximo de onde a exceção ocorre.

itsadok
fonte
7

De erros e exceções # Manipulando exceções - docs.python.org

A try ... exceptdeclaração possui uma elsecláusula opcional , que, quando presente, deve seguir todas, exceto as cláusulas. É útil para o código que deve ser executado se a cláusula try não gerar uma exceção. Por exemplo:

for arg in sys.argv[1:]:
    try:
        f = open(arg, 'r')
    except IOError:
        print 'cannot open', arg
    else:
        print arg, 'has', len(f.readlines()), 'lines'
        f.close()

O uso da cláusula else é melhor do que adicionar código adicional à cláusula try, pois evita a captura acidental de uma exceção que não foi gerada pelo código que está sendo protegido pela instrução try ... exceto.

fedorqui 'Então pare de prejudicar'
fonte
6

Olhando para a referência Python , parece que elseé executado depois tryquando não há exceção. A cláusula else opcional é executada se e quando o controle fluir no final da cláusula try. 2 Exceções na cláusula else não são tratadas pelas cláusulas anteriores, exceto.

Mergulhe no python tem um exemplo em que, se eu entendi direito, no trybloco eles tentam importar um módulo, quando isso falha, você obtém exceção e vincula o padrão, mas quando funciona, você tem a opção de entrar no elsebloco e vincular o que é necessário (consulte link para o exemplo e explicação).

Se você tentou trabalhar em catchbloco, isso poderia gerar outra exceção - acho que é aí que o elsebloco é útil.

stefanB
fonte
4
"Exceções na cláusula else não são tratadas pelas cláusulas anteriores, exceto." Essa é a parte útil. Obrigado.
Georg4
"A cláusula else opcional é executada se e quando o controle sair do final da cláusula try" é outra diferença, pois você pode retornar do trybloco.
Tomer W
4

É isso aí. O bloco 'else' de uma cláusula try-except existe para o código que é executado quando (e somente quando) a operação tentada é bem-sucedida. Pode ser usado e pode ser abusado.

try:
    fp= open("configuration_file", "rb")
except EnvironmentError:
    confdata= '' # it's ok if the file can't be opened
else:
    confdata= fp.read()
    fp.close()

# your code continues here
# working with (possibly empty) confdata

Pessoalmente, gosto e uso quando apropriado. Agrupa semanticamente instruções.

tzot
fonte
2

Talvez um uso possa ser:

#debug = []

def debuglog(text, obj=None):
    " Simple little logger. "
    try:
        debug   # does global exist?
    except NameError:
        pass    # if not, don't even bother displaying
    except:
        print('Unknown cause. Debug debuglog().')
    else:
        # debug does exist.
        # Now test if you want to log this debug message
        # from caller "obj"
        try:
            if obj in debug:
                print(text)     # stdout
        except TypeError:
            print('The global "debug" flag should be an iterable.')
        except:
            print('Unknown cause. Debug debuglog().')

def myfunc():
    debuglog('Made it to myfunc()', myfunc)

debug = [myfunc,]
myfunc()

Talvez isso o leve também a um uso.

DevPlayer
fonte
2

Eu achei a try: ... else:construção útil na situação em que você está executando consultas de banco de dados e registrando os resultados dessas consultas em um banco de dados separado do mesmo tipo / tipo. Digamos que eu tenha muitos threads de trabalho, todos lidando com consultas de banco de dados enviadas a uma fila

#in a long running loop
try:
    query = queue.get()
    conn = connect_to_db(<main db>)
    curs = conn.cursor()
    try:
        curs.execute("<some query on user input that may fail even if sanitized">)
    except DBError:
        logconn = connect_to_db(<logging db>)
        logcurs = logconn.cursor()
        logcurs.execute("<update in DB log with record of failed query")
        logcurs.close()
        logconn.close()
    else:

        #we can't put this in main try block because an error connecting
        #to the logging DB would be indistinguishable from an error in 
        #the mainquery 

        #We can't put this after the whole try: except: finally: block
        #because then we don't know if the query was successful or not

        logconn = connect_to_db(<logging db>)
        logcurs = logconn.cursor()
        logcurs.execute("<update in DB log with record of successful query")
        logcurs.close()
        logconn.close()
        #do something in response to successful query
except DBError:
    #This DBError is because of a problem with the logging database, but 
    #we can't let that crash the whole thread over what might be a
    #temporary network glitch
finally:
    curs.close()
    conn.close()
    #other cleanup if necessary like telling the queue the task is finished

Obviamente, se você pode distinguir entre as possíveis exceções que podem ser lançadas, não é necessário usá-lo, mas se o código que reage a um pedaço de código bem-sucedido pode lançar a mesma exceção que o pedaço bem-sucedido, e você não pode simplesmente deixe a segunda exceção possível ir ou retorne imediatamente com sucesso (o que mataria o encadeamento no meu caso), e isso será útil.

sirlark
fonte
1

elseMuitas vezes, pode existir um bloco para complementar a funcionalidade que ocorre em cada exceptbloco.

try:
    test_consistency(valuable_data)
except Except1:
    inconsistency_type = 1
except Except2:
    inconsistency_type = 2
except:
    # Something else is wrong
    raise
else:
    inconsistency_type = 0

"""
Process each individual inconsistency down here instead of
inside the except blocks. Use 0 to mean no inconsistency.
"""

Nesse caso, inconsistency_typeé definido em cada bloco exceto, para que o comportamento seja complementado no caso sem erro em else.

Claro, estou descrevendo isso como um padrão que pode aparecer em seu próprio código algum dia. Nesse caso específico, você apenas define inconsistency_type0 antes do trybloco.

Wesley
fonte
1

Aqui está outro lugar onde eu gosto de usar esse padrão:

 while data in items:
     try
        data = json.loads(data)
     except ValueError as e:
        log error
     else:
        # work on the `data`
grdvnl
fonte
1
Você pode simplesmente usar continue- o padrão "sair cedo". Isso permite que você elimine a cláusula "else" e seu recuo, facilitando a leitura do código.
usar o seguinte código
1

Um dos cenários de uso em que posso pensar é as exceções imprevisíveis, que podem ser contornadas se você tentar novamente. Por exemplo, quando as operações no bloco try envolvem números aleatórios:

while True:
    try:
        r = random.random()
        some_operation_that_fails_for_specific_r(r)
    except Exception:
        continue
    else:
        break

Mas se a exceção puder ser prevista, você sempre deve escolher a validação antecipadamente em vez de uma exceção. No entanto, nem tudo pode ser previsto, portanto, esse padrão de código tem seu lugar.

NeoWang
fonte
1
Você pode fazer isso colocando o breakinterior tryno final, que é o IMO mais limpo e não precisa do else. Também continuenão é realmente necessário, você pode apenas pass.
Dirbaio 10/06
1

Eu achei elseútil para lidar com um arquivo de configuração possivelmente incorreto:

try:
    value, unit = cfg['lock'].split()
except ValueError:
    msg = 'lock monitoring config must consist of two words separated by white space'
    self.log('warn', msg)
else:
     # get on with lock monitoring if config is ok

Uma exceção na leitura da lockconfiguração desativa o monitoramento de bloqueios e o ValueErrors registra uma mensagem de aviso útil.

usuario
fonte
1

Suponha que sua lógica de programação dependa se um dicionário possui uma entrada com uma determinada chave. Você pode testar o resultado do dict.get(key)uso da if... else...construção ou pode:

try:
    val = dic[key]
except KeyError:
    do_some_stuff()
else:
    do_some_stuff_with_val(val)
Gene
fonte
-1

Eu adicionaria outro caso de uso que parece simples ao lidar com sessões de banco de dados:

    # getting a DB connection 
    conn = db.engine.connect()

    # and binding to a DB session
    session = db.get_session(bind=conn)

    try:
        # we build the query to DB
        q = session.query(MyTable).filter(MyTable.col1 == 'query_val')

        # i.e retrieve one row
        data_set = q.one_or_none()

        # return results
        return [{'col1': data_set.col1, 'col2': data_set.col2, ...}]

    except:
        # here we make sure to rollback the transaction, 
        # handy when we update stuff into DB
        session.rollback()
        raise

    else:
        # when no errors then we can commit DB changes
        session.commit()

    finally:
        # and finally we can close the session
        session.close()
vadimbog
fonte
-17

O else:bloco é confuso e (quase) inútil. Também faz parte das declarações fore while.

Na verdade, mesmo em uma ifdeclaração, o else:pode ser abusado de maneiras realmente terríveis, criando bugs que são muito difíceis de encontrar.

Considere isto.

   if a < 10:
       # condition stated explicitly
   elif a > 10 and b < 10:
       # condition confusing but at least explicit
   else:
       # Exactly what is true here?
       # Can be hard to reason out what condition is true

Pense duas vezes else:. Geralmente é um problema. Evite-o, exceto em uma ifdeclaração e, mesmo assim, considere documentar a elsecondição - para torná-la explícita.

S.Lott
fonte
6
Eu discordaria deste. No bloco "if-elif", "else" é usado como "padrão" seria usado no bloco "case" da linguagem C. É sempre recomendável lidar com maiúsculas e minúsculas "padrão", mesmo que você pense que cobriu todos os casos em várias condições.
1313 Josip
1
@ Josip: usado como um "padrão" pode ser confuso. O problema é definir claramente a condição que é esse "padrão". Uma condição padrão mal definida pode ser a causa raiz do comportamento de buggy. Caso contrário, pode ser uma causa de confusão. Deve ser pensado com muito cuidado em todos os casos, não apenas tente, por e enquanto, mas também.
1350 S.Lott
5
Bem, o código acima é totalmente abstrato e não faz nada significativo, então sim - não é de admirar que seja confuso.
julx
1
@ S.Lott "Isso reduziria o bugginess" - e meu argumento é que isso é falso. Acho que apenas temos uma diferença genuína de opiniões. Os programadores ruins sempre encontram maneiras de escrever programas com erros. Sempre. Bons programadores sempre buscam boas práticas e podem escrever um bom código em praticamente qualquer idioma. Eliminar construções úteis apenas dá menos poder aos bons programadores, enquanto não ajuda particularmente os ruins, pois eles são capazes de inventar um número infinito de maneiras de melhorar as coisas.
julx
5
Considere: if x > 0: return "yes"e if x <= 0: return "no". Agora uma pessoa vem e muda uma das condições para dizer, x > 1mas esquece de mudar a outra. Como é esse número reduzido de bugs que seriam cometidos. if elseas cláusulas às vezes têm muitas linhas separadas. DRY é uma boa prática, muito mais frequentemente do que não, na verdade. (desculpe pelo post duplo).
julx