web-dev-qa-db-fra.com

Gestion des exceptions génériques dans Python la "bonne manière"

Parfois, je me trouve dans la situation où je veux exécuter plusieurs commandes séquentielles comme celles-ci:

try:
    foo(a, b)
except Exception, e:
    baz(e)
try:
    bar(c, d)
except Exception, e:
    baz(e)
...

Ce même modèle se produit lorsque les exceptions doivent simplement être ignorées.

Cela semble redondant et la syntaxe excessive le rend étonnamment difficile à suivre lors de la lecture de code.

En C, j'aurais résolu ce type de problème facilement avec une macro, mais malheureusement, cela ne peut pas être fait en python pur.

Question: Comment puis-je réduire au mieux l'encombrement du code et augmenter la lisibilité du code lorsque je rencontre ce modèle?

29
Sufian

Vous pouvez utiliser l'instruction with si vous avez python 2.5 ou supérieur:

from __future__ import with_statement
import contextlib

@contextlib.contextmanager
def handler():
    try:
        yield
    except Exception, e:
        baz(e)

Votre exemple devient maintenant:

with handler():
    foo(a, b)
with handler():
    bar(c, d)
69
Ryan

Si c'est toujours, toujours le comportement que vous voulez quand une fonction particulière lève une exception, vous pouvez utiliser un décorateur:

def handle_exception(handler):
    def decorate(func):
        def call_function(*args, **kwargs):
            try:
                func(*args, **kwargs)
            except Exception, e:
                handler(e)
        return call_function
    return decorate

def baz(e):
    print(e)

@handle_exception(baz)
def foo(a, b):
    return a + b

@handle_exception(baz)
def bar(c, d):
    return c.index(d)

Usage:

>>> foo(1, '2')
unsupported operand type(s) for +: 'int' and 'str'
>>> bar('steve', 'cheese')
substring not found
14
Jonny Buchanan

S'il s'agit de commandes simples d'une seule ligne, vous pouvez les encapsuler dans lambdas:

for cmd in [
    (lambda: foo (a, b)),
    (lambda: bar (c, d)),
]:
    try:
        cmd ()
    except StandardError, e:
        baz (e)

Vous pouvez envelopper tout cela dans une fonction, donc cela ressemblait à ceci:

ignore_errors (baz, [
    (lambda: foo (a, b)),
    (lambda: bar (c, d)),
])
4
John Millikin

Vous pouvez essayer quelque chose comme ça. Cela ressemble vaguement à une macro C.

class TryOrBaz( object ):
    def __init__( self, that ):
        self.that= that
    def __call__( self, *args ):
        try:
            return self.that( *args )
        except Exception, e:
            baz( e )

TryOrBaz( foo )( a, b )
TryOrBaz( bar )( c, d )
3
S.Lott

La meilleure approche que j'ai trouvée est de définir une fonction comme celle-ci:

def handle_exception(function, reaction, *args, **kwargs):
    try:
        result = function(*args, **kwargs)
    except Exception, e:
        result = reaction(e)
    return result

Mais cela ne semble tout simplement pas correct dans la pratique:

handle_exception(foo, baz, a, b)
handle_exception(bar, baz, c, d)
3
Sufian