2009-03-31 10 views
29

Gibt es eine schönere Art und Weise folgendes tun:Verschachtelte Try-Anweisungen in Python?

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

Es sieht ziemlich böse und ich möchte lieber nicht tun:

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

maximale Effizienz zu erhalten.

+0

Haben Sie Ihre Theorie getestet, dass die zweite Option ineffizient ist? Es würde mich überraschen, wenn es nicht effizienter wäre als das erste. – Oddthinking

+0

Oddthinking ist wahrscheinlich korrekt. Mit hasattr entfällt die Notwendigkeit, eine Ausnahme auszulösen. –

+1

Tatsächlich ruft die Implementierung von hasattr() im Wesentlichen nur getattr() auf und gibt False zurück, wenn eine Ausnahme ausgelöst wird; siehe http://svn.python.org/view/python/tags/r254/Python/bltinmodule.c?view = markup – Miles

Antwort

21

Vielleicht könnten Sie so etwas wie dies versuchen:

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 

Sie würde es so nennen:

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

Dies wird versuchen, die Methoden in der Reihenfolge, wie sie in sind zu nennen in Die Liste. Wenn Sie irgendwelche Argumente zu übergeben wollen, konnte man sie nur wie so entlang nach der Liste übergeben:

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

Wie wäre es mit der Einkapselung der Anrufe in einer Funktion?

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

Warum der "einkapselnde" Teil? nur das 'pass' ist eine nette Idee, scheint mir. – cregox

1

Wenn Sie neuen Stil-Objekt verwenden:

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 

Natürlich, wenn Sie aren‘ Wenn Sie ein neues Objekt verwenden, können Sie versuchen, __dict__ statt __getattribute__.

EDIT: Dieser Code könnte sich als ein schreiendes Durcheinander erweisen. Wenn __getattribute__ oder __dict__ nicht gefunden wird, erraten Sie, welche Art von Fehler aufgetreten ist.

+0

Verwenden Sie unbedingt die Funktion getattr() anstelle der Methode __getattribute__. – Miles

+0

Ich kann die relativen Vorteile von getattr vs __getattribute__ nicht vollständig herausfinden. Es gibt Objekte, für die entweder AttributeError ausgelöst wird und die anderen funktionieren. –

23

Eine leichte Änderung in der Sekunde sieht ziemlich nett und einfach aus. Ich bezweifle wirklich, dass Sie jeden Performance-Unterschied zwischen den beide bemerken, und dies ist ein bisschen schöner als eine verschachtelte try/excepts

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

Die andere sehr lesbare Weise ist zu tun ..

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

    try: 
     return a.method2() 
    except: 
     pass 

    try: 
     return a.method3() 
    except: 
     pass 

    raise AttributeError 

Während es lang ist, ist es sehr offensichtlich, was die Funktion tut. Leistung sollte wirklich kein Problem sein (wenn einige try/except-Anweisungen Ihr Skript merklich verlangsamen, gibt es wahrscheinlich ein größeres Problem mit der Skriptstruktur)

+1

Ich mag die zweite, da sie sehr gut lesbar und einfach ist. Wenn Leistung wirklich ein Problem ist, macht das ursprüngliche Poster wahrscheinlich etwas falsch. –

3

Eine kompakte Lösung:

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

Kompakt, aber möglicherweise falsch. Wenn 'a'' method1' hat, aber * nicht 'method3', dann wird dies fehlschlagen. Das dritte Argument für "getattr" wird ausgewertet, bevor "getattr" aufgerufen wird, was bedeutet, dass dieser Code versucht, 'method3' zu holen, bevor er' method1' und 'method2' betrachtet. Siehe [Ethan Furmans Antwort] (http://stackoverflow.com/a/7971414/33732) für eine sicherere Alternative. –

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

Dies wird method3method2 für method1, dann schauen Sie zuerst, dann. Die Suche wird beendet, sobald einer von ihnen gefunden wird. Wenn keine der Methoden gefunden wird, löst die letzte getattr eine Ausnahme aus.

Verwandte Themen