2010-09-16 9 views
6

Avevo una funzione che restituiva un membro casuale di più gruppi in ordine di preferenza. E 'andato qualcosa di simile:Esiste un'alternativa più ordinata 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" 

Tuttavia, la prima cosa che fa è get_random_foo verificare ci sono ambrose e sollevano un IndexError se non, in modo da there_are_foos è ridondante. Inoltre, è coinvolto un database e l'utilizzo di funzioni separate crea un problema di concorrenza. Di conseguenza, ho riscritto qualcosa di simile:

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" 

Ma trovo questo molto meno leggibile, e come non ho mai avuto motivo di usare pass prima che si sente istintivamente sbagliato.

C'è un modello più ordinato efficiente, o dovrei imparare ad accettare pass?

Nota: vorrei evitare ogni nidificazione in quanto altri tipi possono essere aggiunti in seguito.


Modifica

ringrazia tutti coloro che hanno detto che pass va bene - questo è rassicurante!

anche grazie a chi ha suggerito di sostituire l'eccezione con un valore di ritorno di None. Posso vedere come questo sia uno schema utile, ma direi che è semanticamente sbagliato in questa situazione: alle funzioni è stato chiesto di eseguire un'attività impossibile in modo che dovessero sollevare un'eccezione. Preferisco seguire il comportamento del modulo random (ad esempio random.choice([])).

+1

L'uso di 'dichiarazioni except' è probabilmente l'uso più comune di' pass' in Python. Quindi mi ci abituerei. –

+0

"Ma trovo questo molto meno leggibile ... si sente istintivamente sbagliato" - pensalo come una questione culturale. Sembra strano ora, ma è il modo più pulito di usare la lingua. – detly

risposta

13

che è esattamente come avrei scriverlo. È semplice e ha un senso. Non vedo alcun problema con le dichiarazioni pass.

Se si vuole ridurre la ripetizione e si prevede l'aggiunta di tipi futuro, si potrebbe rotolare questo in su in un ciclo. Poi si potrebbe cambiare il pass ad un continue dichiarazione funzionalmente equivalente, se questo è più piacevole per gli occhi:

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

Se è solo loro due, può sempre e solo ...

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

Se si tratta di più di due, quello che hai scritto sembra perfettamente accettabile.

2

pass va bene (c'è un motivo è in lingua -!), Ma un pass alternativa libera vuole solo un po 'più di nidificazione:

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

di Python Zen (import this dal prompt interprete interattivo) dice "piatta è meglio di annidati", ma di nidificazione è anche in th Il linguaggio che usi quando decidi (presumibilmente di essere illuminato) che puoi fare meglio di quel saggio koan! -) (Come in "se incontri il Buddha sulla strada" ...).

2

Mi sembra un po 'strano che get_random_foo() stia generando un IndexError quando non prende un indice come parametro (ma potrebbe avere più senso nel contesto). Perché non utilizzare lo get_random_foo() o un wrapper, rilevare l'errore e restituire None invece?

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 

Edit: Devo dire che se foo & bar sono oggetti che possono valutare su False (0 o '' dicono) allora il confronto or salterà su di loro che è male

+0

Ho preso la mia stecca dal modulo casuale - prova a eseguire 'random.choice ([])'. Soluzione interessante però, grazie! –

0

Basandosi su Il suggerimento di Peter Gibson, potresti creare una generica funzione wrapper che inghiotte una determinata eccezione. E quindi potresti scrivere una funzione che restituisce come un wrapper generico per un'eccezione fornita. O diamine, per un elenco fornito di eccezioni.

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

Se non si ha realmente bisogno il messaggio di eccezione (solo il 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

Penso che preferirei sollevare esplicitamente l'errore da 'get_random_foo_or_bar' piuttosto che basarsi sull'errore che si verifica per essere lo stesso e che richiede un commento. Inoltre, ciò mi consente di restituire un messaggio di eccezione appropriato da get_random_foo_or_bar'. –

0

E 'necessario che get_random_foo/bar() alzare un IndexError se è in grado di avere successo?

Se tornarono None, si potrebbe fare:

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