Gerando (lançando) manualmente uma exceção no Python


2266

Como posso gerar uma exceção no Python para que possa ser capturada posteriormente através de um exceptbloco?

Respostas:


2938

Como lançar / gerar manualmente uma exceção no Python?

Use o construtor Exception mais específico que se encaixa semanticamente no seu problema .

Seja específico na sua mensagem, por exemplo:

raise ValueError('A very specific bad thing happened.')

Não crie exceções genéricas

Evite criar um genérico Exception. Para capturá-lo, você precisará capturar todas as outras exceções mais específicas que a subclasses.

Problema 1: Ocultando erros

raise Exception('I know Python!') # Don't! If you catch, likely to hide bugs.

Por exemplo:

def demo_bad_catch():
    try:
        raise ValueError('Represents a hidden bug, do not catch this')
        raise Exception('This is the exception you expect to handle')
    except Exception as error:
        print('Caught this error: ' + repr(error))

>>> demo_bad_catch()
Caught this error: ValueError('Represents a hidden bug, do not catch this',)

Problema 2: Não vai pegar

E capturas mais específicas não capturam a exceção geral:

def demo_no_catch():
    try:
        raise Exception('general exceptions not caught by specific handling')
    except ValueError as e:
        print('we will not catch exception: Exception')


>>> demo_no_catch()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in demo_no_catch
Exception: general exceptions not caught by specific handling

Práticas recomendadas: raisedeclaração

Em vez disso, use o construtor Exception mais específico que se encaixa semanticamente no seu problema .

raise ValueError('A very specific bad thing happened')

que também permite com facilidade um número arbitrário de argumentos serem passados ​​para o construtor:

raise ValueError('A very specific bad thing happened', 'foo', 'bar', 'baz') 

Esses argumentos são acessados ​​pelo argsatributo no Exceptionobjeto. Por exemplo:

try:
    some_code_that_may_raise_our_value_error()
except ValueError as err:
    print(err.args)

impressões

('message', 'foo', 'bar', 'baz')    

No Python 2.5, um messageatributo real foi adicionado para BaseExceptionincentivar os usuários a subclassar Exceptions e parar de usar args, mas a introdução messagee a descontinuação original de args foram retraídas .

Práticas recomendadas: exceptcláusula

Quando dentro de uma cláusula de exceção, você pode, por exemplo, registrar que um tipo específico de erro ocorreu e, em seguida, aumentar novamente. A melhor maneira de fazer isso enquanto preserva o rastreamento da pilha é usar uma instrução bare raise. Por exemplo:

logger = logging.getLogger(__name__)

try:
    do_something_in_app_that_breaks_easily()
except AppError as error:
    logger.error(error)
    raise                 # just this!
    # raise AppError      # Don't do this, you'll lose the stack trace!

Não modifique seus erros ... mas se você insistir.

Você pode preservar o stacktrace (e o valor do erro) sys.exc_info(), mas isso é muito mais suscetível a erros e tem problemas de compatibilidade entre o Python 2 e 3 , prefere usar um nu raisepara aumentar novamente.

Para explicar - sys.exc_info()retorna o tipo, valor e retorno.

type, value, traceback = sys.exc_info()

Esta é a sintaxe no Python 2 - observe que isso não é compatível com o Python 3:

    raise AppError, error, sys.exc_info()[2] # avoid this.
    # Equivalently, as error *is* the second object:
    raise sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]

Se desejar, você pode modificar o que acontece com seu novo aumento - por exemplo, definindo novo argspara a instância:

def error():
    raise ValueError('oops!')

def catch_error_modify_message():
    try:
        error()
    except ValueError:
        error_type, error_instance, traceback = sys.exc_info()
        error_instance.args = (error_instance.args[0] + ' <modification>',)
        raise error_type, error_instance, traceback

E preservamos todo o rastreamento enquanto modificamos os argumentos. Observe que essa não é uma prática recomendada e é uma sintaxe inválida no Python 3 (dificultando muito a solução da compatibilidade).

>>> catch_error_modify_message()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in catch_error_modify_message
  File "<stdin>", line 2, in error
ValueError: oops! <modification>

No Python 3 :

    raise error.with_traceback(sys.exc_info()[2])

Novamente: evite manipular manualmente os rastreamentos. É menos eficiente e mais propenso a erros. E se você estiver usando o encadeamento e sys.exc_infopoderá até obter o rastreamento errado (especialmente se estiver usando o tratamento de exceções para o fluxo de controle - o que eu pessoalmente evitaria).

Python 3, encadeamento de exceção

No Python 3, você pode encadear exceções, que preservam os rastreamentos:

    raise RuntimeError('specific message') from error

Estar ciente:

  • esta não permitir a alteração do tipo de erro elevada, e
  • isso não é compatível com o Python 2.

Métodos preteridos:

Eles podem facilmente ocultar e até entrar no código de produção. Você deseja criar uma exceção e, ao fazê-las, uma exceção, mas não a que se destina!

Válido no Python 2, mas não no Python 3 é o seguinte:

raise ValueError, 'message' # Don't do this, it's deprecated!

Válido apenas em versões muito mais antigas do Python (2.4 e inferior), você ainda pode ver pessoas criando strings:

raise 'message' # really really wrong. don't do this.

Em todas as versões modernas, isso realmente gera um TypeError, porque você não está criando um BaseExceptiontipo. Se você não estiver verificando a exceção certa e não tiver um revisor ciente do problema, ele poderá entrar em produção.

Exemplo de uso

Eu levanto Exceções para avisar os consumidores da minha API se eles a estiverem usando incorretamente:

def api_func(foo):
    '''foo should be either 'baz' or 'bar'. returns something very useful.'''
    if foo not in _ALLOWED_ARGS:
        raise ValueError('{foo} wrong, use "baz" or "bar"'.format(foo=repr(foo)))

Crie seus próprios tipos de erro quando apropriado

"Eu quero cometer um erro de propósito, para que ele entre na exceção"

Você pode criar seus próprios tipos de erro, se desejar indicar que algo específico está errado com seu aplicativo, basta subclassificar o ponto apropriado na hierarquia de exceções:

class MyAppLookupError(LookupError):
    '''raise this when there's a lookup error for my app'''

e uso:

if important_key not in resource_dict and not ok_to_be_missing:
    raise MyAppLookupError('resource is missing, and that is not ok.')

19
Obrigado por isso, é exatamente o que eu precisava. O essencial raiseé o que eu precisava para poder executar a depuração de erro personalizada em vários níveis de execução de código sem interromper o rastreamento da pilha.
CaffeineConnoisseur

Esta é uma ótima resposta. Mas ainda trabalho com muito código 2.7 e, muitas vezes, desejo adicionar informações a uma exceção inesperada, como uma posição do arquivo de entrada ou os valores de algumas variáveis, mas mantenho a pilha e a exceção originais. Eu posso registrá-lo, mas às vezes não quero que seja registrado, por exemplo, se o código pai finalmente o manipular. raise sys.exc_info()[0], (sys.exc_info()[1], my_extra_info), sys.exc_info()[2]parece fazer o que eu quero e nunca tive problemas com isso. Mas parece hacky, e não uma prática aceita. Existe uma maneira melhor?
Michael Scheper

2
@brennanyoung Nesse contexto, acho que pode ser confuso gerar um SyntaxError - provavelmente você deve criar uma exceção personalizada. Eu explico como aqui: stackoverflow.com/a/26938914/541136
Aaron Hall

2
Observe que a cotação completa é "Todas as exceções internas que não saem do sistema são derivadas dessa classe. Todas as exceções definidas pelo usuário também devem ser derivadas dessa classe". - Isso significa principalmente que você não deve usar uma das quatro exceções que não derivam Exceptioncomo sua classe pai - você pode subclassificar algo mais específico e deve fazê-lo se fizer sentido.
Aaron Hall

1
No exemplo de " Práticas recomendadas: exceto cláusula ", você usa uma AppErrorexceção indefinida . Pode ser melhor usar um erro AttributeError
interno

530

NÃO FAÇA ISSO . Levantar a nu nãoException é absolutamente a coisa certa a fazer; veja a excelente resposta de Aaron Hall .

Não pode ficar muito mais pitônico do que isso:

raise Exception("I know python!")

Consulte os documentos da instrução raise para python, se desejar obter mais informações.


67
Não por favor! Isso remove o potencial de ser específico sobre o que você captura. É totalmente a maneira errada de fazê-lo. Dê uma olhada na excelente resposta de Aaron Hall em vez desta. Em momentos como este, eu gostaria de poder dar mais de um voto negativo por resposta.
Dawood ibn Kareem

27
@ PeterR É igualmente terrível que tenha tão poucos votos negativos. Para NINGUÉM lendo esta resposta, NÃO FAÇA ISTO NUNCA! A resposta correta é a de Aaron Hall.
Dawood ibn Kareem

6
Eu acho que deveria haver uma explicação mais detalhada sobre por que isso está errado ou tão ruim.
Charlie Parker

9
@CharlieParker Existe. É a primeira parte da resposta de Aaron Hall .
Dinei

5
Por que essa resposta não pode ser sinalizada para exclusão? Já tem 93 votos negativos!
codeforester

55

No Python3, existem 4 sintaxes diferentes para exceções de exclusão:

1. raise exception 
2. raise exception (args) 
3. raise
4. raise exception (args) from original_exception

1. aumentar exceção vs. 2. aumentar exceção (args)

Se você usar raise exception (args) para gerar uma exceção, argsela será impressa quando você imprimir o objeto de exceção - como mostrado no exemplo abaixo.

  #raise exception (args)
    try:
        raise ValueError("I have raised an Exception")
    except ValueError as exp:
        print ("Error", exp)     # Output -> Error I have raised an Exception 



  #raise execption 
    try:
        raise ValueError
    except ValueError as exp:
        print ("Error", exp)     # Output -> Error 

3.raise

raiseA instrução sem nenhum argumento gera novamente a última exceção. Isso é útil se você precisar executar algumas ações após capturar a exceção e depois desejar aumentá-la novamente. Mas, se não havia exceção antes, a raiseinstrução gera TypeErrorException.

def somefunction():
    print("some cleaning")

a=10
b=0 
result=None

try:
    result=a/b
    print(result)

except Exception:            #Output ->
    somefunction()           #some cleaning
    raise                    #Traceback (most recent call last):
                             #File "python", line 8, in <module>
                             #ZeroDivisionError: division by zero

4. gerar exceção (args) de original_exception

Essa instrução é usada para criar o encadeamento de exceções, no qual uma exceção que é gerada em resposta a outra exceção pode conter os detalhes da exceção original - como mostrado no exemplo abaixo.

class MyCustomException(Exception):
pass

a=10
b=0 
reuslt=None
try:
    try:
        result=a/b

    except ZeroDivisionError as exp:
        print("ZeroDivisionError -- ",exp)
        raise MyCustomException("Zero Division ") from exp

except MyCustomException as exp:
        print("MyException",exp)
        print(exp.__cause__)

Resultado:

ZeroDivisionError --  division by zero
MyException Zero Division 
division by zero

7
Por favor note que PEP8 prefere exception(args)maisexception (args)
Gloweye

Também é raise exception(args) from Nonepreciso dizer que a exceção atualmente ativa foi tratada e não é mais interessante. Caso contrário, se você criar uma exceção dentro de um exceptbloco e ele não for tratado, os rastreamentos para ambas as exceções serão mostrados separados pela mensagem "Durante o tratamento da exceção acima, ocorreu outra exceção"
cg909 04/04

35

Para o caso comum em que você precisa lançar uma exceção em resposta a algumas condições inesperadas e que nunca pretende capturar, mas simplesmente falhar rapidamente para permitir que você depure a partir daí, se isso acontecer - o mais lógico parece ser AssertionError:

if 0 < distance <= RADIUS:
    #Do something.
elif RADIUS < distance:
    #Do something.
else:
    raise AssertionError("Unexpected value of 'distance'!", distance)

19
Esse é um caso melhor do ValueErrorque AssertionErrorporque não há problema com uma afirmação (porque nenhuma está sendo feita aqui) - o problema é com um valor. Se você realmente deseja um AssertionErrorneste caso, escreva assert distance > 0, 'Distance must be positive'. Mas você não deve verificar erros dessa maneira, porque as asserções podem ser desativadas ( python -O).
Alquimista de dois bits

1
@ Dois-BitAlquimista Bom argumento. A ideia se perdeu na simplificação, quando escrevi o exemplo simples acima. Em muitos casos semelhantes, é uma condição que não está associada a um valor específico. Em vez disso, o significado é "o fluxo de controle nunca deve chegar aqui".
Evgeni Sergeev

2
@ As asserções Two-BitAlchemist podem ser desativadas, sim, mas você não deve usá-las para verificação de erros?
Evgeni Sergeev

Bem, isto depende. Eu não deixaria esse ser o meu único erro de verificação em um programa que pretendia distribuir. Por outro lado, eu poderia fazer um programa apenas para meus colegas de trabalho e dizer a eles que o usavam por seu próprio risco, se o executassem -O.
Alquimista de dois bits

1
@ Two-BitAlchemist Para mim, o papel das asserções não é a verificação de erros em si (o que é o teste), mas eles criam cercas dentro do código que certos bugs não conseguem passar. Portanto, fica mais fácil rastrear e isolar os bugs, o que inevitavelmente ocorrerá. São apenas bons hábitos que exigem pouco esforço, enquanto o teste exige muito esforço e muito tempo.
Evgeni Sergeev

12

Leia as respostas existentes primeiro, este é apenas um adendo.

Observe que você pode gerar exceções com ou sem argumentos.

Exemplo:

raise SystemExit

sai do programa, mas você pode querer saber o que aconteceu.

raise SystemExit("program exited")

isto imprimirá "programa encerrado" para stderr antes de fechar o programa.


2
Isso não é contra o paradigma OOP? Suponho que o primeiro caso lance a referência de classe e o segundo uma instância do SystemExit. Não raise SystemExit()seria a melhor escolha? Por que o primeiro funciona?
burny

2

Outra maneira de lançar uma exceção é assert. Você pode usar assert para verificar se uma condição está sendo cumprida, caso contrário, ela aumentará AssertionError. Para mais detalhes, dê uma olhada aqui .

def avg(marks):
    assert len(marks) != 0,"List is empty."
    return sum(marks)/len(marks)

mark2 = [55,88,78,90,79]
print("Average of mark2:",avg(mark2))

mark1 = []
print("Average of mark1:",avg(mark1))

2

Apenas para observar: há momentos em que você deseja lidar com exceções genéricas. Se você estiver processando vários arquivos e registrando seus erros, poderá capturar qualquer erro que ocorra em um arquivo, registrá-lo e continuar processando o restante dos arquivos. Nesse caso, um

try:
    foo() 
except Exception as e:
    print(str(e)) # Print out handled error

bloquear uma boa maneira de fazê-lo. Você ainda desejará raiseexceções específicas para saber o que elas significam.


0

Você deve aprender a instrução raise do python para isso. Deve ser mantido dentro do bloco try. Exemplo -

try:
    raise TypeError            #remove TypeError by any other error if you want
except TypeError:
    print('TypeError raised')
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.