2009-10-06 6 views
59

J'ai une compréhension de liste en Python dans laquelle chaque itération peut lancer une exception.Comment puis-je gérer les exceptions dans une compréhension de liste en Python?

Par exemple, si j'ai:

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

[1/egg for egg in eggs] 

Je vais chercher une exception ZeroDivisionError dans le 3ème élément. Comment puis-je gérer cette exception et continuer l'exécution de la compréhension de la liste?

La seule façon que 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 semble un peu lourd pour moi.

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

Note: Ceci est un exemple simple (voir « par exemple » ci-dessus) que je parvins parce que mon exemple réel nécessite un certain contexte. Je ne suis pas intéressé à éviter la division par zéro erreurs mais à gérer les exceptions dans une compréhension de liste.

+2

Il existe un [PEP 463] (https://www.python.org/dev/peps/pep-0463/) pour ajouter une expression pour gérer les exceptions. Dans votre exemple ce serait '[1/egg sauf ZeroDivisionError: None pour egg in (1,3,0,3,2)]'. Mais c'est toujours en mode brouillon. Mon intuition est que ça ne va pas être accepté. – cfi

+1

Notez que pour cet exemple * spécifique *, vous pouvez utiliser un 'ndarray' numpy avec les paramètres appropriés dans 'np. seterr'. Cela donnerait '1/0 = nan', mais je réalise que cela ne généralisera pas aux autres situations où ce besoin se présente – gerrit

Répondre

55

Il n'y a pas 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 littéralement impossible de "gérer les exceptions dans une liste de compréhension" car une liste est une expression contenant d'autres expression, rien de plus (c.-à-d., non instructions, et seules les instructions peuvent attraper/ignorer/gérer les exceptions).

Les appels de fonction sont l'expression, et les corps de fonction peuvent inclure toutes les déclarations que vous voulez, afin de déléguer l'évaluation de la sous-expression d'exception sujette à une fonction, comme vous l'avez remarqué, est une solution de contournement possible (d'autres, lorsque cela est possible, sont des contrôles sur les valeurs qui pourraient provoquer des exceptions, comme suggéré également dans 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, lexicalement dans la compréhension elle-même, vous ne pouvez pas; 2) Pratiquement, vous déléguez le travail à une fonction ou recherchez des valeurs sujettes aux erreurs lorsque cela est possible. Votre affirmation répétée que ce n'est pas une réponse est donc infondée.

+6

Je vois. Donc, une réponse complète serait que je devrais soit: 1. utiliser une fonction 2. ne pas utiliser la compréhension de la liste 3. essayer d'empêcher l'exception plutôt que de la gérer. –

+8

Je ne vois pas "ne pas utiliser les listes de compréhension" comme partie de la réponse à "comment gérer les exceptions dans les listes de compréhension", mais je suppose que vous pourriez raisonnablement considérer cela comme une conséquence possible de "_lexically IN_ le LC, ce n'est pas possible gérer les exceptions ", qui est en effet la première partie de la réponse littérale. –

+0

Pouvez-vous détecter une erreur dans l'expression d'un générateur ou dans la compréhension d'un générateur? – Steve

18

Vous pouvez utiliser

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

cela va simplement sauter des éléments qui sont nuls.

+10

Cela ne répond pas à la question de savoir comment gérer les exceptions dans une compréhension de liste –

+4

euh, oui, ça le fait, ça évite la nécessité de gérer les exceptions ... oui, ce n'est pas la bonne solution tout le temps, mais c'est une chose courante – Peter

+1

Je comprends, je reprends le commentaire (bien que je ne le supprime pas puisque cette courte 'discussion' améliore la réponse.) –

8

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

Votre autre option est de ne pas utiliser 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 

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

+0

Comment pourrais-je utiliser des compréhensions ici? –

+0

@Nathan: vous ne le feriez pas gnibbler dit: * Non, il n'y a pas de meilleur moyen * – SilentGhost

+0

Désolé ... J'ai raté le 'non' dans sa réponse :-) –

58

Je me rends compte de cette question est assez vieux, mais vous pouvez également créer une fonction générale de faire ce genre de chose plus facile:

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, faites en sorte que la fonction par défaut soit celle que vous voulez (par exemple, vous préférez retourner 'None' par défaut).

J'espère que cela vous aidera, vous ou les autres spectateurs de cette question!

Note: en python 3, je ferais seulement le mot-clé d'argument 'handle', et le mettrais à la fin de la liste d'arguments. Cela rendrait en fait passer des arguments et ce par le biais d'une capture beaucoup plus naturelle.

+1

extrêmement utile, merci. Bien que je sois d'accord avec les commentaires théoriques, cela montre une approche pratique pour résoudre un problème que j'ai eu à plusieurs reprises. – Paul

+1

Réponse de Greate. Un mod que je suggère est de passer 'args' et' kwargs' à gérer aussi bien. De cette façon, vous pourriez renvoyer 'egg' au lieu d'un' 0' codé en dur, ou une exception comme vous le faites. –

+1

Vous pouvez également utiliser le type d'exception en tant qu'argument facultatif (les types d'exception peuvent-ils être paramétrés?), De sorte que les exceptions inattendues soient levées au lieu d'ignorer toutes les exceptions. – 00prometheus

1

Je pense qu'une fonction d'aide, comme suggéré par celui qui pose la question initiale et Bryan Head aussi, est bonne et pas encombrante du tout. 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 les boucles for. Cependant, je 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 êtes en contrôle total pour continuer comme vous le souhaitez.

Questions connexes