2009-03-31 8 views
29

c'è un modo più bello di fare quanto segue:Dichiarazioni di prova nidificate in python?

try: 
    a.method1() 
except AttributeError: 
    try: 
     a.method2() 
    except AttributeError: 
     try: 
      a.method3() 
     except AttributeError: 
      raise 

Sembra piuttosto brutto e non preferirei fare:

if hasattr(a, 'method1'): 
    a.method1() 
else if hasattr(a, 'method2'): 
    a.method2() 
else if hasattr(a, 'method3'): 
    a.method3() 
else: 
    raise AttributeError 

per mantenere la massima efficienza.

+0

Hai messo alla prova la tua teoria secondo cui la seconda opzione è inefficiente? Mi sorprenderebbe se non fosse più efficiente del primo. – Oddthinking

+0

Probabilmente è probabile che lo smantellamento sia corretto. hasattr elimina la necessità di sollevare un'eccezione. –

+1

In realtà, l'implementazione di hasattr() essenzialmente chiama getattr() e restituisce False se viene sollevata un'eccezione; vedi http://svn.python.org/view/python/tags/r254/Python/bltinmodule.c?view = markup – Miles

risposta

21

Forse si potrebbe provare qualcosa di simile:

def call_attrs(obj, attrs_list, *args): 
    for attr in attrs_list: 
     if hasattr(obj, attr): 
      bound_method = getattr(obj, attr) 
      return bound_method(*args) 

    raise AttributeError 

si potrebbe chiamare in questo modo:

call_attrs(a, ['method1', 'method2', 'method3']) 

Questo cercherà di chiamare i metodi nell'ordine in cui sono in in la lista. Se si voleva passare alcun argomento, si può solo li passare lungo dopo l'elenco in questo modo:

call_attrs(a, ['method1', 'method2', 'method3'], arg1, arg2) 
4

Come incapsulare le chiamate in una funzione?

def method_1_2_or_3(): 
    try: 
     a.method1() 
     return 
    except AttributeError: 
     pass 
    try: 
     a.method2() 
     return 
    except AttributeError: 
     pass 
    try: 
     a.method3() 
    except AttributeError: 
     raise 
+1

Perché la parte "incapsulante"? solo il 'pass' è un'idea abbastanza carina, mi sembra. – cregox

1

Se si utilizza oggetto di nuovo stile:

methods = ('method1','method2','method3') 
for method in methods: 
    try: 
     b = a.__getattribute__(method) 
    except AttributeError: 
     continue 
    else: 
     b() 
     break 
else: 
    # re-raise the AttributeError if nothing has worked 
    raise AttributeError 

Naturalmente, se si aren' Utilizzando un oggetto di nuovo stile, è possibile provare __dict__ anziché __getattribute__.

MODIFICA: questo codice potrebbe rivelarsi un pasticcio urlante. Se non si trova __getattribute__ o __dict__, si indovini quale tipo di errore viene generato.

+0

Utilizzare decisamente la funzione getattr() al posto del metodo __getattribute__. – Miles

+0

Non riesco a capire del tutto i vantaggi relativi di getattr vs __getattribute__. Esistono oggetti per i quali entrambi generano AttributeError e l'altro funzionerà. –

23

Un leggero cambiamento al secondo sembra piuttosto bello e semplice. Ho davvero dubbi che noterete alcuna differenza di prestazioni tra i due, e questo è un po 'più bello di una prova nidificato/excepts

def something(a): 
    for methodname in ['method1', 'method2', 'method3']: 
     try: 
      m = getattr(a, methodname) 
     except AttributeError: 
      pass 
     else: 
      return m() 
    raise AttributeError 

L'altro modo molto leggibile è quello di fare ..

def something(a): 
    try: 
     return a.method1() 
    except: 
     pass 

    try: 
     return a.method2() 
    except: 
     pass 

    try: 
     return a.method3() 
    except: 
     pass 

    raise AttributeError 

Mentre è lungo, è molto ovvio ciò che sta facendo la funzione .. Le prestazioni in realtà non dovrebbero essere un problema (se alcuni tentativi/eccetto le istruzioni rallentano notevolmente lo script, probabilmente c'è un problema più grande con la struttura dello script)

+1

Mi piace il secondo perché è molto leggibile e diretto. Se le prestazioni sono davvero un problema, il poster originale probabilmente sta facendo qualcosa di sbagliato. –

3

Una soluzione compatta:

getattr(a, 'method1', 
    getattr(a, 'method2', 
     getattr(a, 'method3')))() 
+1

Compatto, ma probabilmente sbagliato. Se 'a' ha' method1' ma * non * ha 'method3', allora fallirà. Il terzo argomento di 'getattr' viene valutato prima che venga chiamato' getattr', il che significa che questo codice cerca di recuperare 'method3' prima che consideri mai' method1' e 'method2'. Vedi [risposta di Ethan Furman] (http://stackoverflow.com/a/7971414/33732) per un'alternativa più sicura. –

5
method = (
     getattr(a, 'method1', None) or 
     getattr(a, 'method2', None) or 
     getattr(a, 'method3') 
     ) 
method() 

Questo cercherà prima method1, quindi method2, quindi method3. La ricerca si interromperà non appena viene trovata una di esse. Se nessuno dei metodi viene trovato, l'ultimo getattr solleverà un'eccezione.

Problemi correlati