web-dev-qa-db-fra.com

Lever manuellement une exception dans Python

Comment puis-je déclencher une exception dans Python afin qu'elle puisse ensuite être interceptée via un bloc except?

1981
TIMEX

Comment lancer/lever manuellement une exception en Python?

tilisez le constructeur Exception le plus spécifique qui correspond sémantiquement à votre problème .

Soyez précis dans votre message, par exemple:

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

Ne pas soulever d'exceptions génériques

Évitez de déclencher une exception générique. Pour l'attraper, vous devrez attraper toutes les autres exceptions plus spécifiques qui le sous-classent.

Problème 1: Cacher des bugs

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

Par exemple:

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

Problème 2: Ne pas attraper

et des captures plus spécifiques ne prendront pas l'exception générale:

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

Pratiques exemplaires: déclaration raise

Au lieu de cela, utilisez le constructeur Exception le plus spécifique qui correspond sémantiquement à votre problème .

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

ce qui permet aussi de manière pratique un nombre arbitraire d'arguments à transmettre au constructeur:

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

L'accès à ces arguments s'effectue par l'attribut args de l'objet Exception. Par exemple:

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

empreintes

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

Dans Python 2.5, un attribut réel message a été ajouté à BaseException afin d'encourager les utilisateurs à sous-classer Exceptions et à cesser d'utiliser args, mais l'introduction de message et la dépréciation initiale des arguments a été retirée .

Meilleures pratiques: clause except

Lorsque vous vous trouvez dans une clause except, vous voudrez peut-être, par exemple, consigner qu'un type d'erreur particulier s'est produit, puis le relancer. La meilleure façon de procéder tout en préservant la trace de la pile consiste à utiliser une instruction bare raise. Par exemple:

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!

Ne modifiez pas vos erreurs ... mais si vous insistez.

Vous pouvez conserver le stacktrace (et la valeur d'erreur) avec sys.exc_info(), mais c'est beaucoup plus sujet aux erreurs et a des problèmes de compatibilité entre Python 2 et 3 , préférez utiliser un élément nu raise pour le relancer.

Pour expliquer - la sys.exc_info() renvoie le type, la valeur et la trace.

type, value, traceback = sys.exc_info()

C'est la syntaxe dans Python 2 - notez que ceci n'est pas compatible avec 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]

Si vous le souhaitez, vous pouvez modifier l’évolution de votre nouvelle relance, par exemple. définir de nouveaux arguments pour l'instance:

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

Et nous avons préservé l'intégralité de la trace en modifiant les arguments. Notez que ceci n’est pas pas une pratique exemplaire et que sa syntaxe est non valide dans Python 3 (il est beaucoup plus difficile de conserver la compatibilité).

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

Dans Python :

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

Encore une fois: évitez de manipuler manuellement les retraits. C'est moins efficace et plus sujet aux erreurs. Et si vous utilisez le threading et sys.exc_info, vous risquez même de vous tromper (notamment si vous utilisez la gestion des exceptions pour le contrôle du flux - ce que j'aurais tendance à éviter personnellement).

Python 3, chaînage d'exceptions

Dans Python 3, vous pouvez chaîner des exceptions qui préservent les traces:

    raise RuntimeError('specific message') from error

Savoir:

  • ceci permet de modifier le type d'erreur généré, et
  • ceci est pas compatible avec Python 2.

Méthodes obsolètes:

Ceux-ci peuvent facilement se cacher et même entrer dans le code de production. Vous voulez déclencher une exception, et les faire lèvera une exception , mais pas celle voulue!

Valide dans Python 2, mais pas dans Python est le suivant:

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

Seulement valide dans des versions beaucoup plus anciennes de Python (2.4 et inférieur), vous pouvez toujours voir des gens élever des chaînes:

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

Dans toutes les versions modernes, cela soulèvera en réalité une erreur TypeError, car vous n'élèverez pas un type BaseException. Si vous ne recherchez pas la bonne exception et si aucun réviseur n'est au courant du problème, cela pourrait entrer en production.

Exemple d'utilisation

Je soulève des exceptions pour avertir les consommateurs de mon API s'ils l'utilisent de manière incorrecte:

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

Créez vos propres types d'erreur quand à propos

"Je veux faire une erreur exprès, pour que cela passe dans l'exception"

Vous pouvez créer vos propres types d'erreur. Si vous souhaitez indiquer un problème spécifique dans votre application, il vous suffit de sous-classer le point approprié dans la hiérarchie des exceptions:

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

et utilisation:

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

NE FAITES PAS CELA . Élever un Exception nu est absolument pas la bonne chose à faire; voir excellente réponse d'Aaron Hall à la place.

Vous ne pouvez pas obtenir beaucoup plus de Pythonic que ceci:

raise Exception("I know python!")

Voir la documentation de l'instruction de relance pour python si vous souhaitez plus d'informations.

532
Gabriel Hurley

Dans Python3, il existe 4 syntaxes différentes pour les exceptions rasing:

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

1. Levée exception vs. 2. levée exception (args)

Si vous utilisez raise exception (args) pour déclencher une exception, alors la args sera imprimée lors de l'impression de l'objet exception - comme illustré dans l'exemple ci-dessous.

  #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 

. augmentation

L'instruction raise sans aucun argument relance la dernière exception. Ceci est utile si vous devez exécuter certaines actions après avoir capturé l'exception, puis vouloir la relancer. Mais s'il n'y avait pas d'exception auparavant, l'instruction raise lève TypeError Exception.

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. Déclenche une exception (args) depuis original_exception

Cette instruction est utilisée pour créer un chaînage d'exceptions dans lequel une exception déclenchée en réponse à une autre exception peut contenir les détails de l'exception d'origine, comme illustré dans l'exemple ci-dessous.

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

Sortie:

ZeroDivisionError --  division by zero
MyException Zero Division 
division by zero
35
N Randhawa

Dans le cas habituel où vous devez lever une exception en réponse à des conditions inattendues et que vous n’avez jamais l’intention de capturer, mais simplement d’échouer rapidement pour vous permettre de déboguer à partir de là si cela se produit - le plus logique semble être AssertionError:

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

Lisez d'abord les réponses existantes, ceci n'est qu'un addenda.

Notez que vous pouvez générer des exceptions avec ou sans arguments.

Exemple:

raise SystemExit

quitte le programme mais vous voudrez peut-être savoir ce qui est arrivé.Vous pouvez donc utiliser ceci.

raise SystemExit("program exited")

ceci affichera "programme terminé" sur stderr avant de fermer le programme.

11
Anant Prakash

Une autre façon de lancer une exception est assert . Vous pouvez utiliser assert pour vérifier qu'une condition est remplie, sinon elle déclenchera AssertionError. Pour plus de détails jeter un oeil ici .

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))
0
Rehan Haider

Notez juste: il y a des moments où vous voulez manipuler des exceptions génériques. Si vous traitez un groupe de fichiers et enregistrez vos erreurs, vous voudrez peut-être détecter toute erreur qui se produit pour un fichier, le consigner et poursuivre le traitement du reste des fichiers. Dans ce cas, un

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

bloquer un bon moyen de le faire. Vous souhaiterez quand même _ raise des exceptions spécifiques afin de savoir ce qu’elles signifient.

0
markemus