2010-09-16 14 views
6

Tenía una función que devolvía un miembro aleatorio de varios grupos en orden de preferencia. Fue algo como esto:¿Existe una alternativa más ordenada a `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" 

Sin embargo, lo primero que hace es verificar get_random_foo hay Foos y plantean una IndexError si no, por lo there_are_foos es redundante. Además, una base de datos está involucrada y el uso de funciones separadas crea un problema de concurrencia. En consecuencia, Reescribí que algo como esto:

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" 

Pero Me parece mucho menos legible, y ya que nunca he tenido razón para utilizar pass antes de que se siente instictively mal.

¿Existe un patrón más eficiente, o debo aprender a aceptar pass?

Nota: Me gustaría evitar cualquier anidación ya que se pueden agregar otros tipos más adelante.


Editar

Gracias a todos los que dijeron que pass está bien - eso es tranquilizador!

También gracias a aquellos que sugirieron reemplazar la excepción con un valor de retorno de None. Puedo ver cómo este es un patrón útil, pero yo diría que es semánticamente incorrecto en esta situación: se les ha pedido a las funciones que realicen una tarea imposible, por lo que deben hacer una excepción. Prefiero seguir el comportamiento del módulo random (por ejemplo, random.choice([])).

+1

El uso de 'declaraciones except' es probablemente el uso más común de' pass' en Python. Entonces me acostumbraría. –

+0

"Pero me parece mucho menos legible ... se siente instintivamente mal", piense en ello como un problema cultural. Parece extraño ahora, pero es la forma más limpia de usar el lenguaje. – detly

Respuesta

13

Así es exactamente como lo escribiría. Es simple y tiene sentido. No veo ningún problema con las declaraciones pass.

Si desea reducir la repetición y anticipa agregar tipos futuros, puede enrollar esto en un bucle. Posteriormente, se podría cambiar la pass a un continue comunicado funcionalmente equivalente, si eso es más agradable a los ojos:

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 es sólo los dos, siempre se puede simplemente ...

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

Si se trata de más de dos, lo que ha escrito parece perfectamente aceptable.

2

pass está muy bien (hay una razón es en el lenguaje -!), Sino una de Transmisión alternativa libre sólo se necesita un poco más de anidación:

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 desde el símbolo del intérprete interactivo) dice "piso es mejor que anidado", pero también de anidación es en º ¡El lenguaje, para que lo uses cuando decidas (supuestamente iluminado) que puedes hacerlo mejor que ese sabio koan! -) (Como en "si te encuentras con el Buda en el camino" ...).

2

Me parece un poco raro que get_random_foo() genere un error de índice cuando no toma un índice como parámetro (pero podría tener más sentido en contexto). ¿Por qué no tener get_random_foo(), o un contenedor, detectar el error y devolver None en su lugar?

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 

Editar: Debo mencionar que si foo & bar son objetos que puedan evaluar en False (0 o '', por ejemplo) entonces la comparación or saltará por encima de ellos que es malo

+0

Me inspiré en el módulo aleatorio, intente ejecutar 'random.choice ([])'. ¡Solución interesante, gracias! –

0

Sobre la base de Sugerencia de Peter Gibson, podría crear una función de envoltura genérica que se trague una excepción dada. Y luego podría escribir una función que devuelva como un contenedor genérico para una excepción proporcionada. O bien, para una lista siempre provista de excepciones.

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 realmente no necesita el mensaje de excepción (sólo el tipo):

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

Creo que preferiría plantear el error explícitamente de 'get_random_foo_or_bar' en lugar de confiar en que el error sea el mismo y necesite un comentario. También eso me permite devolver un mensaje de excepción apropiado de 'get_random_foo_or_bar'. –

0

¿Es necesario que get_random_foo/bar() elevar una IndexError si es incapaz de tener éxito?

si regresaban Ninguno, usted podría hacer:

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