Ao escrever código, geralmente quero fazer algo assim:
try:
foo()
except FooError:
handle_foo()
else:
try:
bar()
except BarError:
handle_bar()
else:
try:
baz()
except BazError:
handle_baz()
else:
qux()
finally:
cleanup()
Obviamente, isso é completamente ilegível. Mas está expressando uma idéia relativamente simples: execute uma série de funções (ou trechos de código curto), com um manipulador de exceção para cada uma, e pare assim que uma função falhar. Eu imagino que o Python poderia fornecer açúcar sintático para esse código, talvez algo como isto:
# NB: This is *not* valid Python
try:
foo()
except FooError:
handle_foo()
# GOTO finally block
else try:
bar()
except BarError:
handle_bar()
# ditto
else try:
baz()
except BazError:
handle_baz()
# ditto
else:
qux()
finally:
cleanup()
Se nenhuma exceção for gerada, isso será equivalente a foo();bar();baz();qux();cleanup()
. Se as exceções são geradas, elas são tratadas pelo manipulador de exceções apropriado (se houver) e pulamos para cleanup()
. Em particular, se bar()
gerar a FooError
ou BazError
, a exceção não será capturada e será propagada para o chamador. Isso é desejável, de modo que apenas capturamos exceções que realmente esperamos tratar.
Independentemente da feiura sintática, esse tipo de código é apenas uma má idéia em geral? Se sim, como você o refatoraria? Imagino que os gerentes de contexto possam ser usados para absorver parte da complexidade, mas não entendo realmente como isso funcionaria no caso geral.
fonte
handle_*
funções?Respostas:
O que
handle_foo
faz? Existem algumas coisas que normalmente fazemos nos blocos de manipulação de exceções.with
Parece-me que você está fazendo algo estranho no tratamento de exceções. Sua pergunta aqui é um sintoma simples do uso de exceções de maneira incomum. Você não está caindo no padrão típico, e é por isso que isso se tornou estranho.
Sem uma idéia melhor do que você está fazendo nessas
handle_
funções, é tudo o que posso dizer.fonte
handle_
funções podem ser facilmente trechos curtos de código que (digamos) registram o erro e retornam valores de fallback, ou geram novas exceções ou fazem várias outras coisas. Enquanto isso, ofoo()
,bar()
, etc. funções poderiam também ser pequenos trechos de código. Por exemplo, podemos terspam[eggs]
e precisamos pegarKeyError
.return
ed ouraise
d alguma coisa, o problema que você menciona não surgiria. O restante da função seria ignorado automaticamente. De fato, uma maneira simples de resolver seu problema seria retornar em todos os blocos de exceção. O código é estranho porque você não está fazendo uma fiança ou aumento habitual para indicar que está desistindo.foo
deveria estar fazendo sua própria limpeza?foo
está sinalizando que algo deu errado e não tem conhecimento de qual deve ser o procedimento de limpeza correto.Parece que você tem uma sequência de comandos que podem gerar uma exceção que precisa ser tratada antes de retornar. Tente agrupar seu código e manipulação de exceções em locais separados. Eu acredito que isso faz o que você pretende.
fonte
try:
bloco. Nesse caso, eu ficaria preocupado embar()
gerar aFooError
, que seria tratado incorretamente com esse código.Existem algumas maneiras diferentes, dependendo do que você precisa.
Aqui está uma maneira com loops:
Aqui está uma maneira com uma saída após o error_handler:
Pessoalmente, acho que a versão em loop é mais fácil de ler.
fonte
Antes de tudo, o uso apropriado de
with
muitas vezes pode reduzir ou até eliminar grande parte do código de manipulação de exceções, melhorando a capacidade de manutenção e a legibilidade.Agora, você pode reduzir o aninhamento de várias maneiras; outros pôsteres já forneceram alguns, então aqui está minha própria variação:
fonte