177 votes

Comment gérer les exceptions dans les listes de compréhension ?

J'ai réalisé une compréhension de liste en Python dans laquelle chaque itération peut lever une exception.

Par exemple si je l'ai fait :

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

[1/egg for egg in eggs]

J'obtiendrai un ZeroDivisionError exception dans le 3ème élément.

Comment puis-je gérer cette exception et poursuivre l'exécution de la compréhension de la liste ?

La seule solution à laquelle je pense est d'utiliser une fonction d'aide :

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.

Existe-t-il une meilleure façon de procéder en Python ?

Remarque : Il s'agit d'un exemple simple (voir " par exemple ") que j'ai inventé parce que mon exemple réel nécessite un certain contexte. Ce qui m'intéresse, ce n'est pas d'éviter les erreurs de division par zéro, mais de gérer les exceptions dans la compréhension d'une liste.

174voto

Bryan Head Points 3077

Je sais 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 entendu définir la fonction de gestion par défaut comme vous le souhaitez (par exemple, vous préférez renvoyer "None" par défaut).

Note : en python 3, je ferais de l'argument 'handle' un mot-clé seulement, et le placerais à la fin de la liste d'arguments. Cela rendrait le passage d'arguments et autres par catch beaucoup plus naturel.

Mise à jour (9 ans plus tard...) : Pour Python 3, je voulais juste dire switch *args et handle Vous pouvez donc spécifier des arguments à la fonction sans spécifier de handle. Une commodité mineure :

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

Ceci est utile lors de l'utilisation d'une fonction définie dans la compréhension :

from math import log    
eggs = [1,3,0,3,2]
[catch(log, egg) for egg in eggs]
[0.0, 1.0986122886681098, ValueError('math domain error'), 1.0986122886681098, 0.6931471805599453]

Sous la version Python 2, nous aurions dû passer en handle avant le egg .

135voto

Alex Martelli Points 330805

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

Les appels de fonction sont des expressions, et le corps des fonctions peut inclure toutes les déclarations que vous souhaitez, de sorte que déléguer l'évaluation de la sous-expression sujette à l'exception à une fonction, comme vous l'avez remarqué, est une solution de contournement possible (d'autres solutions, lorsqu'elles sont possibles, sont des vérifications sur les valeurs susceptibles de provoquer des exceptions, comme cela a également été suggéré dans d'autres réponses).

Les réponses correctes à la question "comment gérer les exceptions dans une liste de compréhension" expriment toutes une partie de cette vérité : 1) littéralement, c'est-à-dire lexicalement dans la compréhension elle-même, on ne peut pas ; 2) pratiquement, on délègue le travail à une fonction ou on vérifie les valeurs sujettes aux erreurs lorsque c'est possible. Votre affirmation répétée selon laquelle il ne s'agit pas d'une réponse est donc sans fondement.

22voto

Peter Points 38320

Vous pouvez utiliser

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

cela permet simplement d'ignorer les éléments qui sont à zéro.

13voto

gnibbler Points 103484

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

L'autre possibilité 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

C'est à vous de décider si c'est plus encombrant ou non.

7voto

Elmex80s Points 1780

Je pense qu'une fonction d'aide, telle que suggérée par celui qui a posé la question initiale et par Bryan Head également, est une bonne chose et n'est pas du tout encombrante. Une seule ligne de code magique qui fait tout le travail n'est pas toujours possible, donc une fonction d'aide est une solution parfaite si l'on veut éviter de devoir utiliser une fonction d'aide. for boucles. Cependant, je la modifierais pour qu'elle devienne celle-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

Le résultat sera le suivant [(1/1, None), (1/3, None), (None, ZeroDivisionError), (1/3, None), (1/2, None)] . Avec cette réponse, vous êtes en pleine possession de vos moyens pour continuer comme vous l'entendez.

Alternative :

def spam2(egg):
    try:
        return 1/egg 
    except ZeroDivisionError:
        # handle division by zero error        
        return ZeroDivisionError

Oui, l'erreur est renvoyée et non levée.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X