96 Stimmen

hasattr() vs. try-except-Block zur Behandlung nicht vorhandener Attribute

if hasattr(obj, 'attribute'):
    # do somthing

gegen

try:
    # access obj.attribute
except AttributeError, e:
    # deal with AttributeError

Welche sollte bevorzugt werden und warum?

1 Stimmen

95voto

ZeD Punkte 999

Gibt es Bänke, die den Leistungsunterschied verdeutlichen?

timeit ist dein Freund

$ python -mtimeit -s 'class C(object): a = 4
c = C()' 'hasattr(c, "nonexistent")'
1000000 loops, best of 3: 1.87 usec per loop
$ python -mtimeit -s 'class C(object): a = 4
c = C()' 'hasattr(c, "a")'
1000000 loops, best of 3: 0.446 usec per loop
$ python -mtimeit -s 'class C(object): a = 4
c = C()' 'try:
 c.a
except:
 pass'
1000000 loops, best of 3: 0.247 usec per loop
$ python -mtimeit -s 'class C(object): a = 4
c = C()' 'try:
 c.nonexistent
except:
 pass'
100000 loops, best of 3: 3.13 usec per loop
$

       |positive|negative
hasattr|  0.446 |  1.87 
try    |  0.247 |  3.13

16 Stimmen

+1 für die Bereitstellung interessanter, greifbarer Zahlen. Der "try" ist in der Tat effizient, wenn er den Normalfall enthält (d. h. wenn eine Python-Ausnahme wirklich außergewöhnlich ist).

1 Stimmen

Ich bin mir nicht sicher, wie diese Ergebnisse zu interpretieren sind. Was ist hier schneller, und um wie viel?

2 Stimmen

@StevenM.Vascellaro: Wenn das Attribut existiert, try ist etwa doppelt so schnell wie hasattr() . Wenn das nicht der Fall ist, try ist etwa 1,5x langsamer als hasattr() (und beide sind wesentlich langsamer, als wenn das Attribut vorhanden ist). Dies liegt wahrscheinlich daran, dass auf dem glücklichen Weg, try tut kaum etwas (Python zahlt bereits für den Overhead der Ausnahmen, unabhängig davon, ob Sie sie verwenden), aber hasattr() erfordert eine Namenssuche und einen Funktionsaufruf. Auf dem unglücklichen Pfad müssen sie beide eine Ausnahmebehandlung und einen goto mais hasattr() macht es in C und nicht in Python-Bytecode.

88voto

Alex Martelli Punkte 805329

hasattr intern und führt schnell die gleiche Aufgabe aus wie der try/except Block: Es handelt sich um ein sehr spezifisches, optimiertes Werkzeug für eine einzige Aufgabe und sollte daher, wenn möglich, der sehr universellen Alternative vorgezogen werden.

8 Stimmen

Außer, dass Sie noch den try/catch-Block benötigen, um Race Conditions zu behandeln (wenn Sie Threads verwenden).

1 Stimmen

Oder der spezielle Fall, auf den ich gerade gestoßen bin: ein django OneToOneField ohne Wert: hasattr(obj, field_name) gibt False zurück, aber es gibt ein Attribut mit field_name: es wird einfach ein DoesNotExist-Fehler ausgegeben.

3 Stimmen

Beachten Sie, dass hasattr wird alle Ausnahmen auffangen in Python 2.x. Siehe meine Antwort für ein Beispiel und die triviale Umgehung.

32voto

poolie Punkte 8882

Es gibt eine dritte und oft bessere Alternative:

attr = getattr(obj, 'attribute', None)
if attr is not None:
     print attr

Vorteile:

  1. getattr hat nicht die schlechte das von Martin Geiser aufgezeigte Verhalten beim Schlucken von Ausnahmen - in alten Pythons, hasattr verschluckt sogar eine KeyboardInterrupt .

  2. Der normale Grund, warum Sie prüfen, ob das Objekt ein Attribut hat, ist, dass Sie das Attribut verwenden können, und das führt natürlich dazu.

  3. Das Attribut wird atomar ausgelesen und ist vor anderen Threads, die das Objekt verändern, sicher. (Wenn dies jedoch ein wichtiges Anliegen ist, sollten Sie erwägen, das Objekt zu sperren, bevor Sie darauf zugreifen).

  4. Es ist kürzer als try/finally und oft kürzer als hasattr .

  5. Eine breite except AttributeError Block kann andere AttributeErrors als der, den Sie erwarten, was zu verwirrendem Verhalten führen kann.

  6. Der Zugriff auf ein Attribut ist langsamer als der Zugriff auf eine lokale Variable (insbesondere wenn es sich nicht um ein einfaches Instanzattribut handelt). (Obwohl, um ehrlich zu sein, Mikro-Optimierung in Python ist oft ein Narr's Errand).

Eine Sache, auf die Sie achten sollten, ist, wenn Sie sich für den Fall interessieren, dass obj.attribute auf None gesetzt ist, müssen Sie einen anderen Sentinel-Wert verwenden.

1 Stimmen

+1 - Dies steht in einer Reihe mit dict.get('my_key', 'default_value') und sollte besser bekannt sein

1 Stimmen

Ideal für häufige Anwendungsfälle, in denen Sie das Vorhandensein prüfen und das Attribut mit Standardwert verwenden möchten.

0 Stimmen

Keine Notwendigkeit für if attr is not None: . Nur if attr: ist ausreichend.

20voto

Martin Geisler Punkte 71257

Ich verwende fast immer hasattr : In den meisten Fällen ist es die richtige Wahl.

Problematisch ist der Fall, dass eine Klasse die __getattr__ : hasattr wird alle Ausnahmen auffangen statt nur zu fangen AttributeError wie Sie es erwarten. Mit anderen Worten, der folgende Code druckt b: False auch wenn es angemessener wäre, eine ValueError Ausnahme:

class X(object):
    def __getattr__(self, attr):
        if attr == 'a':
            return 123
        if attr == 'b':
            raise ValueError('important error from your database')
        raise AttributeError

x = X()
print 'a:', hasattr(x, 'a')
print 'b:', hasattr(x, 'b')
print 'c:', hasattr(x, 'c')

Der wichtige Fehler ist damit verschwunden. Dies war behoben in Python 3.2 ( Ausgabe9666 ) wobei hasattr fängt jetzt nur noch AttributeError .

Eine einfache Abhilfe besteht darin, eine Dienstprogrammfunktion wie die folgende zu schreiben:

_notset = object()

def safehasattr(thing, attr):
    return getattr(thing, attr, _notset) is not _notset

Das lässt getattr mit der Situation umgehen und kann dann die entsprechende Ausnahme auslösen.

2 Stimmen

Dies war auch ein wenig verbessert in Python2.6, so dass hasattr wird zumindest nicht erwischt KeyboardInterrupt usw.

0 Stimmen

Oder, stattdessen safehasattr verwenden Sie einfach getattr um den Wert in eine lokale Variable zu kopieren, wenn Sie ihn verwenden wollen, was fast immer der Fall ist.

0 Stimmen

@poolie Das ist schön, das wusste ich nicht hasattr auf diese Weise verbessert worden war.

13voto

Roee Adler Punkte 32367

Ich würde sagen, es hängt davon ab, ob Ihre Funktion Objekte ohne das Attribut akzeptieren kann konstruktionsbedingt z.B. wenn Sie zwei Aufrufer der Funktion haben, von denen einer ein Objekt mit dem Attribut und der andere ein Objekt ohne dieses Attribut liefert.

Wenn der einzige Fall, in dem Sie ein Objekt ohne das Attribut erhalten, auf einen Fehler zurückzuführen ist, würde ich empfehlen, den Ausnahmemechanismus zu verwenden, auch wenn er langsamer sein kann, weil ich glaube, dass es ein saubereres Design ist.

Unterm Strich: Ich denke, es ist eher ein Problem des Designs und der Lesbarkeit als ein Problem der Effizienz.

1 Stimmen

+1 dafür, dass du darauf bestehst, warum "try" für Leute, die den Code lesen, eine Bedeutung hat :)

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X