try - Élever manuellement(lancer) une exception en Python




try except python 3 (4)

Comment puis-je déclencher une exception dans Python afin qu'il puisse être capturé plus tard via un bloc except ?


Comment lancer / lever manuellement une exception en Python?

Utilisez 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 déclencher d'exceptions génériques

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

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

et les captures plus spécifiques n'atteindront 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

Meilleures pratiques: déclaration de 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 également de transmettre un nombre arbitraire d'arguments au constructeur:

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

Ces arguments sont accessibles par l'attribut args sur l'objet Exception. Par exemple:

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

imprime

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

Dans Python 2.5, un attribut de message réel a été ajouté à BaseException en faveur d'encourager les utilisateurs à sous-classer les exceptions et à cesser d'utiliser les args , mais l'introduction du message et la dépréciation originale des arguments a été rétractée .

Meilleures pratiques: except clause

Lorsque vous vous trouvez dans une clause except, vous pouvez, par exemple, consigner qu'un type spécifique d'erreur s'est produit, puis relancer. La meilleure façon de le faire tout en préservant la trace de la pile consiste à utiliser une instruction raise simple. 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 la stacktrace (et la valeur de l'erreur) avec sys.exc_info() , mais cela est beaucoup plus sujet aux erreurs et a des problèmes de compatibilité entre Python 2 et 3 , préférez utiliser une raise nue pour sur-relancer.

Pour expliquer: sys.exc_info() renvoie le type, la valeur et la traceback.

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 ce qui se passe avec votre nouvelle relance, par exemple en définissant 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 conservé tout le retraçage tout en modifiant les arguments. Notez que ceci n'est pas une bonne pratique et c'est une syntaxe invalide dans Python 3 (rendant la compatibilité de conservation beaucoup plus difficile à contourner).

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

En Python 3 :

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

Encore une fois: évitez de manipuler manuellement les retraçages. C'est moins efficace et plus sujet aux erreurs. Et si vous utilisez threading et sys.exc_info vous risquez d'obtenir un mauvais suivi (en particulier si vous utilisez la gestion des exceptions pour le flux de contrôle - 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 retraçages:

    raise RuntimeError('specific message') from error

Savoir:

  • cela permet de changer le type d'erreur, et
  • ceci n'est pas compatible avec Python 2.

Méthodes déconseillées:

Ceux-ci peuvent facilement se cacher et même entrer dans le code de production. Vous voulez lever une exception, et les faire lever une exception, mais pas celui prévu!

Valide dans Python 2, mais pas dans Python 3 est la suivante:

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

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

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

Dans toutes les versions modernes, cela déclenchera réellement un TypeError, parce que vous n'élevez pas un type BaseException. Si vous ne vérifiez pas la bonne exception et que vous n'avez pas d'examinateur conscient du problème, il pourrait entrer en production.

Exemple d'utilisation

Je lève des exceptions pour avertir les consommateurs de mon API s'ils ne l'utilisent pas correctement:

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 sur le but, de sorte qu'il irait dans l'exception"

Vous pouvez créer vos propres types d'erreurs, si vous voulez indiquer que quelque chose de spécifique est incorrect avec votre application, il 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.')

Ne fais pas ça . Élever une Exception nue n'est absolument pas la bonne chose à faire; voyez plutôt share .

Vous ne pouvez pas obtenir beaucoup plus pythonique que ceci:

raise Exception("I know python!")

Voyez les docs de déclaration de raise pour python si vous souhaitez plus d'informations.


Pour le cas courant où vous devez lancer une exception en réponse à des conditions inattendues, et que vous n'avez jamais l'intention d'intercepter, mais simplement échouer rapidement pour vous permettre de déboguer à partir de là si jamais cela arrive - 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)

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

Notez que vous pouvez lever des exceptions avec ou sans arguments.

Exemple:

raise SystemExit

quitte le programme, mais vous voudrez peut-être savoir ce qui s'est passé. Donc, vous pouvez l'utiliser.

raise SystemExit("program exited")

cela affichera "programme sorti" sur stderr avant de fermer le programme.





exception-handling