Qual é o motivo da existência do try-except-else?
Um try
bloco permite que você lide com um erro esperado. O except
bloco deve capturar apenas exceções com as quais você está preparado para lidar. Se você manipular um erro inesperado, seu código poderá fazer a coisa errada e ocultar bugs.
Uma else
cláusula será executada se não houver erros e, ao não executar esse código no try
bloco, você evita a captura de um erro inesperado. Mais uma vez, capturar um erro inesperado pode ocultar bugs.
Exemplo
Por exemplo:
try:
try_this(whatever)
except SomeException as the_exception:
handle(the_exception)
else:
return something
O conjunto "try, except" possui duas cláusulas opcionais else
e finally
. Então é verdade try-except-else-finally
.
else
avaliará apenas se não houver exceção do try
bloco. Permite simplificar o código mais complicado abaixo:
no_error = None
try:
try_this(whatever)
no_error = True
except SomeException as the_exception:
handle(the_exception)
if no_error:
return something
portanto, se compararmos else
com a alternativa (que pode criar bugs), veremos que ela reduz as linhas de código e podemos ter uma base de código mais legível, sustentável e com menos bugs.
finally
finally
será executado independentemente do que for, mesmo que outra linha esteja sendo avaliada com uma declaração de retorno.
Dividido com pseudo-código
Pode ajudar a decompor isso, na menor forma possível que demonstre todos os recursos, com comentários. Suponha que esse pseudocódigo sintaticamente correto (mas não executável, a menos que os nomes sejam definidos) esteja em uma função.
Por exemplo:
try:
try_this(whatever)
except SomeException as the_exception:
handle_SomeException(the_exception)
# Handle a instance of SomeException or a subclass of it.
except Exception as the_exception:
generic_handle(the_exception)
# Handle any other exception that inherits from Exception
# - doesn't include GeneratorExit, KeyboardInterrupt, SystemExit
# Avoid bare `except:`
else: # there was no exception whatsoever
return something()
# if no exception, the "something()" gets evaluated,
# but the return will not be executed due to the return in the
# finally block below.
finally:
# this block will execute no matter what, even if no exception,
# after "something" is eval'd but before that value is returned
# but even if there is an exception.
# a return here will hijack the return functionality. e.g.:
return True # hijacks the return in the else clause above
É verdade que, em vez disso, poderíamos incluir o código no else
bloco try
, onde seria executado se não houvesse exceções, mas e se esse código em si gerar uma exceção do tipo que estamos capturando? Deixá-lo no try
bloco esconderia esse bug.
Queremos minimizar as linhas de código no try
bloco para evitar capturar exceções que não esperávamos, sob o princípio de que, se nosso código falhar, queremos que falhe alto. Essa é uma prática recomendada .
Entendo que exceções não são erros
No Python, a maioria das exceções são erros.
Podemos visualizar a hierarquia de exceções usando pydoc. Por exemplo, no Python 2:
$ python -m pydoc exceptions
ou Python 3:
$ python -m pydoc builtins
Nos dará a hierarquia. Podemos ver que muitos tipos de Exception
erros, embora o Python os use para coisas como finalizar for
loops ( StopIteration
). Esta é a hierarquia do Python 3:
BaseException
Exception
ArithmeticError
FloatingPointError
OverflowError
ZeroDivisionError
AssertionError
AttributeError
BufferError
EOFError
ImportError
ModuleNotFoundError
LookupError
IndexError
KeyError
MemoryError
NameError
UnboundLocalError
OSError
BlockingIOError
ChildProcessError
ConnectionError
BrokenPipeError
ConnectionAbortedError
ConnectionRefusedError
ConnectionResetError
FileExistsError
FileNotFoundError
InterruptedError
IsADirectoryError
NotADirectoryError
PermissionError
ProcessLookupError
TimeoutError
ReferenceError
RuntimeError
NotImplementedError
RecursionError
StopAsyncIteration
StopIteration
SyntaxError
IndentationError
TabError
SystemError
TypeError
ValueError
UnicodeError
UnicodeDecodeError
UnicodeEncodeError
UnicodeTranslateError
Warning
BytesWarning
DeprecationWarning
FutureWarning
ImportWarning
PendingDeprecationWarning
ResourceWarning
RuntimeWarning
SyntaxWarning
UnicodeWarning
UserWarning
GeneratorExit
KeyboardInterrupt
SystemExit
Um comentarista perguntou:
Digamos que você tenha um método que envia uma API externa e deseja manipular a exceção em uma classe fora do wrapper da API. Você simplesmente retorna e do método na cláusula exceto onde e é o objeto de exceção?
Não, você não retorna a exceção, basta aumentá-la com um simples raise
para preservar o rastreamento de pilha.
try:
try_this(whatever)
except SomeException as the_exception:
handle(the_exception)
raise
Ou, no Python 3, você pode criar uma nova exceção e preservar o backtrace com o encadeamento de exceções:
try:
try_this(whatever)
except SomeException as the_exception:
handle(the_exception)
raise DifferentException from the_exception
Eu elaboro em minha resposta aqui .