web-dev-qa-db-fra.com

Comment gérer les exceptions dans une compréhension de liste en Python?

J'ai une compréhension de liste en Python dans laquelle chaque itération peut générer une exception. 

Par exemple , si j'ai:

eggs = (1,3,0,3,2)

[1/Egg for Egg in eggs]

Je vais avoir une exception ZeroDivisionError dans le 3ème élément.

Comment puis-je gérer cette exception et continuer à exécuter la compréhension de liste?

La seule façon dont je peux penser est d'utiliser une fonction d'assistance:

def spam(Egg):
    try:
        return 1/Egg
    except ZeroDivisionError:
        # handle division by zero error
        # leave empty for now
        pass

Mais cela me semble un peu lourd.

Y a-t-il une meilleure façon de faire cela en Python?

Note: Ceci est un exemple simple (voir "pour exemple" ci-dessus) que j'ai fabriqué parce que mon exemple réel nécessite un contexte. Je ne suis pas intéressé par les erreurs de division par zéro, mais par la gestion des exceptions dans une compréhension de liste.

78
Nathan Fellman

Il n'y a pas d'expression intégrée en Python qui vous permet d'ignorer une exception (ou de renvoyer des valeurs alternatives & c en cas d'exceptions), il est donc impossible, littéralement, de "gérer les exceptions dans une compréhension de liste" car une compréhension de liste est une expression contenant d’autres expressions, rien de plus (c’est-à-dire no instructions, et seules les instructions peuvent intercepter/ignorer/gérer des exceptions).

Les appels de fonction sont des expressions et les corps des fonctions peuvent inclure toutes les instructions souhaitées. Par conséquent, la délégation de l'évaluation de la sous-expression sujette aux exceptions à une fonction constitue une solution de contournement réalisable (les autres, le cas échéant, sont les suivantes). vérifie les valeurs susceptibles de provoquer des exceptions, comme le suggèrent d’autres réponses).

Les réponses correctes à la question "Comment gérer les exceptions dans une compréhension de liste" expriment toutes une partie de toute cette vérité: 1) littéralement, c'est-à-dire de manière lexique DANS la compréhension elle-même, vous ne pouvez pas; 2) dans la pratique, vous déléguez le travail à une fonction ou recherchez des valeurs sujettes aux erreurs lorsque cela est réalisable. Votre affirmation répétée selon laquelle il ne s'agit pas d'une réponse n'est donc pas fondée.

70
Alex Martelli

Je me rends compte que cette question est assez ancienne, mais vous pouvez également créer une fonction générale pour faciliter ce genre de choses:

def catch(func, handle=lambda e : e, *args, **kwargs):
    try:
        return func(*args, **kwargs)
    except Exception as e:
        return handle(e)

Ensuite, dans votre compréhension:

eggs = (1,3,0,3,2)
[catch(lambda : 1/Egg) for Egg in eggs]
[1, 0, ('integer division or modulo by zero'), 0, 0]

Vous pouvez bien sûr faire en sorte que le handle par défaut fonctionne comme vous le souhaitez (disons que vous préférez renvoyer 'Aucun' par défaut).

J'espère que cela vous aidera ou aidera les futurs téléspectateurs à cette question!

Remarque: en python 3, je créerais uniquement le mot clé argument 'handle' et le placerais à la fin de la liste des arguments. Cela rendrait en réalité les arguments passants et autres par la capture beaucoup plus naturels.

78
Bryan Head

Vous pouvez utiliser

[1/Egg for Egg in eggs if Egg != 0]

cela ignorera simplement les éléments nuls.

19
Peter

Non, il n'y a pas de meilleur moyen. Dans de nombreux cas, vous pouvez utiliser l'évitement comme le fait Peter

Votre autre option est de ne pas utiliser les compréhensions

eggs = (1,3,0,3,2)

result=[]
for Egg in eggs:
    try:
        result.append(Egg/0)
    except ZeroDivisionError:
        # handle division by zero error
        # leave empty for now
        pass

A vous de décider si cela est plus lourd ou non

7
John La Rooy

Je pense qu'une fonction d'assistance, suggérée par celui qui pose la question initiale et par Bryan Head également, est bonne et pas lourde du tout. Une seule ligne de code magique qui fait tout le travail n'est tout simplement pas toujours possible. Une fonction d'assistance est donc la solution idéale si vous voulez éviter les boucles for. Cependant je le modifierais à celui-ci:

# A modified version of the helper function by the Question starter 
def spam(Egg):
    try:
        return 1/Egg, None
    except ZeroDivisionError as err:
        # handle division by zero error        
        return None, err

La sortie sera cette [(1/1, None), (1/3, None), (None, ZeroDivisionError), (1/3, None), (1/2, None)]. Avec cette réponse, vous avez le contrôle total pour continuer comme vous le souhaitez. 

3
Elmex80s