2010-09-16 3 views
6

J'ai eu une fonction qui a retourné un membre aléatoire de plusieurs groupes dans l'ordre de préférence. Il a quelque chose comme ceci:Existe-t-il une alternative plus propre à `except: pass`?

def get_random_foo_or_bar(): 
    "I'd rather have a foo than a bar." 

    if there_are_foos(): 
     return get_random_foo() 

    if there_are_bars(): 
     return get_random_bar() 

    raise IndexError, "No foos, no bars" 

Cependant, la première chose que get_random_foo fait est de vérifier il y a foos et soulèvent un IndexError sinon, si there_are_foos est redondant. De plus, une base de données est impliquée et l'utilisation de fonctions séparées crée un problème de simultanéité. Par conséquent, je réécrite quelque chose comme ceci:

def get_random_foo_or_bar(): 
    "Still prefer foos." 

    try: 
     return get_random_foo() 
    except IndexError: 
     pass 

    try: 
     return get_random_bar() 
    except IndexError: 
     pass 

    raise IndexError, "No foos, no bars" 

Mais je trouve cela beaucoup moins lisible, et comme je ne l'ai jamais eu raison d'utiliser pass avant qu'il ne se sent instictively mal.

Y a-t-il un modèle plus efficace ou devrais-je apprendre à accepter pass?

Remarque: Je souhaite éviter toute imbrication car d'autres types peuvent être ajoutés ultérieurement.


Modifier

Merci tout le monde qui a dit que pass est très bien - c'est rassurant!

Merci également à ceux qui ont suggéré de remplacer l'exception par une valeur de retour de None. Je peux voir comment c'est un modèle utile, mais je dirais que c'est sémantiquement faux dans cette situation: les fonctions ont été invitées à effectuer une tâche impossible, donc ils devraient lever une exception. Je préfère suivre le comportement du module random (par exemple random.choice([])).

+1

L'utilisation dans 'déclarations except' est probablement l'utilisation la plus courante de' pass' en Python. Donc, je m'y habituerais. –

+0

"Mais je trouve cela beaucoup moins lisible ... ça sent instinctivement mal" - je pense qu'il s'agit d'un problème culturel. Cela semble étrange maintenant, mais c'est la façon la plus propre d'utiliser la langue. – detly

Répondre

13

C'est exactement comme ça que je l'écrirais. C'est simple et c'est logique. Je ne vois aucun problème avec les instructions pass.

Si vous souhaitez réduire la répétition et anticiper l'ajout de types futurs, vous pouvez l'enrouler en boucle. Ensuite, vous pouvez changer le pass une déclaration fonctionnellement équivalent continue, si c'est plus agréable à vos yeux:

for getter in (get_random_foo, get_random_bar): 
    try: 
     return getter() 
    except IndexError: 
     continue # Ignore the exception and try the next type. 

raise IndexError, "No foos, no bars" 
1

Si c'est juste ces deux, pourrait toujours juste ...

try: 
    return get_random_foo() 
except IndexError: 
    try: 
     return get_random_bar() 
    except IndexError: 
     raise IndexError, "No foos, no bars" 

Si elle est plus de deux, ce que vous avez écrit semble parfaitement acceptable.

2

pass est très bien (il y a une raison il est dans la langue -), mais de passe alternative libre prend juste un peu plus d'imbrication:

try: return get_random_foo() 
except IndexError: 
    try: return get_random_bar() 
    except IndexError: 
     raise IndexError "no foos, no bars" 

Zen de Python (import this de l'invite de l'interpréteur interactif) dit « plat est mieux que imbriqué », mais l'imbrication est également dans e la langue, pour que vous l'utilisiez quand vous décidez (probablement d'être éclairé) que vous pouvez faire mieux que ce sage koan! -) (Comme dans, "si vous rencontrez le Bouddha sur la route" ...).

2

Il me semble un peu étrange que get_random_foo() lève une erreur IndexError quand elle ne prend pas un index en tant que param (mais cela pourrait avoir plus de sens en contexte). Pourquoi ne pas avoir get_random_foo(), ou un wrapper, attraper l'erreur et retourner None à la place?

def get_random_foo_wrapper(): 
    try: 
     return get_random_foo() 
    except IndexError: 
     return None 

def get_random_foo_or_bar(): 
    "I'd rather have a foo than a bar." 

    return get_random_foo_wrapper() or get_random_bar_wrapper() or None 

Modifier: Je dois mentionner que si foo & bar sont des objets qui peuvent évaluer la valeur False (0 ou « » dire), la comparaison or va sauter sur eux qui est BAD

+0

J'ai pris mon repère du module aléatoire - essayez d'exécuter 'random.choice ([])'. Solution intéressante cependant, merci! –

0

bâtiment sur La suggestion de Peter Gibson, vous pouvez créer une fonction wrapper générique qui avale une exception donnée. Et puis vous pouvez écrire une fonction qui renvoie un tel wrapper générique pour une exception fournie. Ou diable, pour un fourni liste d'exceptions.

def maketrap(*exceptions): 
    def trap(func, *args, **kwargs): 
     try: 
      return func(*args, **kwargs) 
     except exceptions: 
      return None 
    return trap 

def get_random_foo_or_bar(): 
    mytrap = maketrap(IndexError) 
    return mytrap(get_random_foo) or mytrap(get_random_bar) or None 
0

Si vous n'avez pas vraiment besoin du message d'exception (juste le type):

def get_random_foo_or_bar(): 
    try: 
     return get_random_foo() 
    except IndexError: 
     return get_random_bar() # if failing at this point, 
            # the whole function will raise IndexError 
+0

Je pense que je préférerais augmenter l'erreur explicitement de 'get_random_foo_or_bar' plutôt que de me fier à l'erreur qui se passe pour être la même et avoir besoin d'un commentaire. Cela me permet également de renvoyer un message d'exception approprié de 'get_random_foo_or_bar'. –

0

Est-il nécessaire que get_random_foo/bar() soulever une IndexError si elle est incapable de réussir?

S'ils sont revenus Aucun, vous pouvez faire:

def get_random_foo_or_bar(): 
    return get_random_foo() or get_random_bar()