Ich schreibe meinen eigenen Container, der durch Attributaufrufe Zugriff auf ein Wörterbuch im Inneren gewähren muss. Die typische Verwendung des Behälters wäre wie folgt:

dict_container = DictContainer()
dict_container['foo'] = bar
...
print dict_container.foo

Ich weiß, dass es vielleicht dumm ist, so etwas zu schreiben, aber das ist die Funktionalität, die ich bereitstellen muss. Ich habe darüber nachgedacht, dies folgendermaßen umzusetzen:

def __getattribute__(self, item):
    try:
        return object.__getattribute__(item)
    except AttributeError:
        try:
            return self.dict[item]
        except KeyError:
            print "The object doesn't have such attribute"

Ich bin mir nicht sicher, ob verschachtelte Try / Except-Blöcke eine gute Vorgehensweise sind. Eine andere Möglichkeit wäre die Verwendung von hasattr() und has_key():

def __getattribute__(self, item):
        if hasattr(self, item):
            return object.__getattribute__(item)
        else:
            if self.dict.has_key(item):
                return self.dict[item]
            else:
                raise AttributeError("some customised error")

Oder um einen von ihnen zu verwenden und einen Fangblock wie folgt zu versuchen:

def __getattribute__(self, item):
    if hasattr(self, item):
        return object.__getattribute__(item)
    else:
        try:
            return self.dict[item]
        except KeyError:
            raise AttributeError("some customised error")

Welche Option ist am pythonischsten und elegantesten?

182
Michal 10 Juni 2013 im 03:37

11 Antworten

Beste Antwort

Ihr erstes Beispiel ist vollkommen in Ordnung. Sogar die offiziellen Python-Dokumente empfehlen diesen Stil, der als EAFP bekannt ist.

Persönlich vermeide ich lieber das Verschachteln, wenn es nicht notwendig ist:

def __getattribute__(self, item):
    try:
        return object.__getattribute__(item)
    except AttributeError:
        pass  # fallback to dict
    try:
        return self.dict[item]
    except KeyError:
        raise AttributeError("The object doesn't have such attribute") from None

PS. has_key() ist in Python 2 schon lange veraltet. Verwenden Sie stattdessen item in self.dict.

163
lqc 9 Juni 2013 im 23:52

Laut der Dokumentation ist es besser, mehrere Ausnahmen durch Tupel oder ähnliches zu behandeln ::

import sys

try:
    f = open('myfile.txt')
    s = f.readline()
    i = int(s.strip())
except IOError as e:
    print "I/O error({0}): {1}".format(e.errno, e.strerror)
except ValueError:
    print "Could not convert data to an integer."
except:
    print "Unexpected error:", sys.exc_info()[0]
    raise
3
Blairg23 14 Jän. 2016 im 18:15

Wenn try-exception-finally im finally-Block verschachtelt ist, bleibt das Ergebnis von "child" finally erhalten. Ich habe noch keine offizielle Erklärung gefunden, aber das folgende Code-Snippet zeigt dieses Verhalten in Python 3.6.

def f2():
    try:
        a = 4
        raise SyntaxError
    except SyntaxError as se:
        print('log SE')
        raise se from None
    finally:
        try:
            raise ValueError
        except ValueError as ve:
            a = 5
            print('log VE')
            raise ve from None
        finally:
            return 6       
        return a

In [1]: f2()
log SE
log VE
Out[2]: 6
1
Guanghua Shu 25 Feb. 2019 im 20:48

Eine Sache, die ich gerne vermeiden möchte, ist das Auslösen einer neuen Ausnahme beim Umgang mit einer alten. Das Lesen der Fehlermeldungen ist verwirrend.

Zum Beispiel habe ich in meinem Code ursprünglich geschrieben

try:
    return tuple.__getitem__(self, i)(key)
except IndexError:
    raise KeyError(key)

Und ich habe diese Nachricht bekommen.

>>> During handling of above exception, another exception occurred.

Was ich wollte war folgendes:

try:
    return tuple.__getitem__(self, i)(key)
except IndexError:
    pass
raise KeyError(key)

Es hat keinen Einfluss darauf, wie Ausnahmen behandelt werden. In jedem Codeblock wäre ein KeyError abgefangen worden. Dies ist lediglich eine Frage des Erhaltens von Stilpunkten.

2
Steve Zelaznik 2 Feb. 2015 im 15:04

Für Ihr spezielles Beispiel müssen Sie sie nicht verschachteln. Wenn der Ausdruck im Block try erfolgreich ist, wird die Funktion zurückgegeben, sodass Code nach dem gesamten Versuch / Ausnahme-Block nur ausgeführt wird, wenn der erste Versuch fehlschlägt. Sie können also einfach Folgendes tun:

def __getattribute__(self, item):
    try:
        return object.__getattribute__(item)
    except AttributeError:
        pass
    # execution only reaches here when try block raised AttributeError
    try:
        return self.dict[item]
    except KeyError:
        print "The object doesn't have such attribute"

Sie zu verschachteln ist nicht schlecht, aber ich habe das Gefühl, es flach zu lassen, um die Struktur klarer zu machen: Sie probieren nacheinander eine Reihe von Dingen aus und geben die erste zurück, die funktioniert.

Im Übrigen möchten Sie vielleicht darüber nachdenken, ob Sie hier wirklich __getattribute__ anstelle von __getattr__ verwenden möchten. Die Verwendung von __getattr__ vereinfacht die Arbeit, da Sie wissen, dass der normale Attributsuchprozess bereits fehlgeschlagen ist.

8
BrenBarn 9 Juni 2013 im 23:52

In Java ist es in der Tat eine schlechte Praxis, Ausnahmen für die Flusskontrolle zu verwenden (hauptsächlich, weil Ausnahmen das JVM zwingen, Ressourcen zu sammeln (mehr hier)), haben Sie in Python zwei wichtige Prinzipien: Duck Typing und EAFP. Dies bedeutet im Grunde, dass Sie aufgefordert werden, ein Objekt so zu verwenden, wie Sie es sich vorstellen, und damit umzugehen, wenn die Dinge nicht so sind.

Zusammenfassend wäre das einzige Problem, dass Ihr Code zu stark eingerückt wird. Wenn Sie Lust dazu haben, versuchen Sie, einige der Verschachtelungen zu vereinfachen, wie von lqc vorgeschlagen

19
Community 23 Mai 2017 im 11:54

Ich denke nicht, dass es darum geht, pythonisch oder elegant zu sein. Es geht darum, Ausnahmen so weit wie möglich zu verhindern. Ausnahmen sollen Fehler behandeln, die in Code oder Ereignissen auftreten können, über die Sie keine Kontrolle haben. In diesem Fall haben Sie die volle Kontrolle, wenn Sie prüfen, ob ein Element ein Attribut oder ein Wörterbuch ist. Vermeiden Sie daher verschachtelte Ausnahmen und bleiben Sie bei Ihrem zweiten Versuch.

0
owobeid 9 Juni 2013 im 23:49

Seien Sie vorsichtig - in diesem Fall wird zuerst finally berührt, ABER auch übersprungen.

def a(z):
    try:
        100/z
    except ZeroDivisionError:
        try:
            print('x')
        finally:
            return 42
    finally:
        return 1


In [1]: a(0)
x
Out[1]: 1
6
Sławomir Lenart 26 März 2018 im 17:59

Meiner Meinung nach wäre dies die pythonischste Art, damit umzugehen, obwohl und weil es Ihre Frage zur Diskussion stellt. Beachten Sie, dass dies __getattr__() anstelle von __getattribute__() definiert, da dies bedeutet, dass nur die "speziellen" Attribute behandelt werden müssen, die im internen Wörterbuch gespeichert sind.

def __getattr__(self, name):
    """only called when an attribute lookup in the usual places has failed"""
    try:
        return self.my_dict[name]
    except KeyError:
        raise AttributeError("some customized error message")
7
martineau 10 Juni 2013 im 09:46

In Python ist es einfacher, um Vergebung zu bitten als um Erlaubnis. Schwitzen Sie nicht bei der Behandlung verschachtelter Ausnahmen.

(Außerdem verwendet has* sowieso fast immer Ausnahmen unter dem Deckmantel.)

4
Ignacio Vazquez-Abrams 9 Juni 2013 im 23:40

Ein gutes und einfaches Beispiel für verschachtelte Versuche / Ausnahmen könnte das Folgende sein:

import numpy as np

def divide(x, y):
    try:
        out = x/y
    except:
        try:
            out = np.inf * x / abs(x)
        except:
            out = np.nan
    finally:
        return out

Probieren Sie nun verschiedene Kombinationen aus und Sie erhalten das richtige Ergebnis:

divide(15, 3)
# 5.0

divide(15, 0)
# inf

divide(-15, 0)
# -inf

divide(0, 0)
# nan

[Natürlich haben wir Numpy, also müssen wir diese Funktion nicht erstellen]

2
Ravaging Care 4 Sept. 2019 im 14:13