2009-03-31 2 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.