if hasattr(obj, 'attribute'):
# do somthing
vs.
try:
# access obj.attribute
except AttributeError, e:
# deal with AttributeError
Welches sollte bevorzugt werden und warum?
Antworten:
hasattr
führt intern und schnell dieselbe Aufgabe wie der try/except
Block aus: Es ist ein sehr spezifisches, optimiertes Tool für eine Aufgabe und sollte daher gegebenenfalls der sehr universellen Alternative vorgezogen werden.
hasattr
werden alle Ausnahmen fangen in Python 2.x. In meiner Antwort finden Sie ein Beispiel und die einfache Problemumgehung.
try
kann vermitteln, dass die Operation funktionieren sollte . Obwohl try
die Absicht nicht immer so ist, ist sie üblich, so dass sie als besser lesbar angesehen werden kann.
Gibt es Bänke, die Leistungsunterschiede veranschaulichen?
Timeit, es 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
try
ist es ungefähr doppelt so schnell wie hasattr()
. Wenn dies nicht der Fall ist, try
ist es ungefähr 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 Pfad try
kaum etwas unternommen wird (Python zahlt bereits für den Overhead von Ausnahmen, unabhängig davon, ob Sie sie verwenden), hasattr()
erfordert jedoch eine Namenssuche und einen Funktionsaufruf. Auf dem unglücklichen Pfad müssen beide eine Ausnahmebehandlung und a ausführen goto
, hasattr()
dies jedoch in C und nicht in Python-Bytecode.
Es gibt eine dritte und oft bessere Alternative:
attr = getattr(obj, 'attribute', None)
if attr is not None:
print attr
Vorteile:
getattr
hat nicht das schlechte Ausnahme-Schluckverhalten, auf das Martin Geiser hingewiesen hat - in alten Pythons hasattr
wird sogar ein schlucken KeyboardInterrupt
.
Der normale Grund, warum Sie prüfen, ob das Objekt ein Attribut hat, besteht darin, dass Sie das Attribut verwenden können, und dies führt natürlich dazu.
Das Attribut wird atomar abgelesen und ist vor anderen Threads geschützt, die das Objekt ändern. (Wenn dies jedoch ein wichtiges Anliegen ist, sollten Sie das Objekt vor dem Zugriff sperren.)
Es ist kürzer als try/finally
und oft kürzer als hasattr
.
Ein breiter except AttributeError
Block kann andere AttributeErrors
als den von Ihnen erwarteten fangen , was zu verwirrendem Verhalten führen kann.
Der Zugriff auf ein Attribut ist langsamer als der Zugriff auf eine lokale Variable (insbesondere, wenn es sich nicht um ein einfaches Instanzattribut handelt). (Um ehrlich zu sein, ist die Mikrooptimierung in Python oft ein Kinderspiel.)
Wenn Sie sich für den Fall interessieren obj.attribute
, in dem Keine festgelegt ist, müssen Sie einen anderen Sentinel-Wert verwenden.
Ich benutze fast immer hasattr
: Es ist in den meisten Fällen die richtige Wahl.
Der problematische Fall ist , wenn eine Klasse überschreibt __getattr__
: hasattr
werden alle Ausnahmen fangen , anstatt nur zu fangen , AttributeError
wie Sie es erwarten. Mit anderen Worten, der folgende Code wird gedruckt b: False
, obwohl es angemessener wäre, eine ValueError
Ausnahme zu sehen :
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 wurde in Python 3.2 ( Problem 9666 ) behoben, wo hasattr
jetzt nur noch Fänge auftreten AttributeError
.
Eine einfache Problemumgehung besteht darin, eine Dienstprogrammfunktion wie die folgende zu schreiben:
_notset = object()
def safehasattr(thing, attr):
return getattr(thing, attr, _notset) is not _notset
Lassen Sie uns getattr
die Situation behandeln und dann die entsprechende Ausnahme auslösen.
hasattr
zumindest nicht fangen KeyboardInterrupt
usw.
safehasattr
einfach getattr
in eine lokale Variable, wenn Sie ihn verwenden möchten, was Sie fast immer sind.
hasattr
das so verbessert wurde.
hasattr
, und ging, um nachzusehen. Wir hatten einige lustige BZR-Bugs, bei denen Hasattr gerade geschluckt hat ^ C.
Ich würde sagen, es hängt davon ab, ob Ihre Funktion Objekte ohne das Attribut beabsichtigt , z. B. wenn Sie zwei Aufrufer für die Funktion haben, von denen einer ein Objekt mit dem Attribut und der andere ein Objekt ohne das Attribut bereitstellt.
Wenn der einzige Fall, in dem Sie ein Objekt ohne das Attribut erhalten, auf einen Fehler zurückzuführen ist, würde ich die Verwendung des Ausnahmemechanismus empfehlen, auch wenn dieser möglicherweise langsamer ist, da ich glaube, dass es sich um ein saubereres Design handelt.
Fazit: Ich denke, es ist eher ein Design- und Lesbarkeitsproblem als ein Effizienzproblem.
Wenn das Fehlen des Attributs keine Fehlerbedingung ist, hat die Ausnahmebehandlungsvariante ein Problem: Sie fängt auch AttributeErrors ab, die möglicherweise intern beim Zugriff auf obj.attribute auftreten (z. B. weil attribute eine Eigenschaft ist, sodass beim Zugriff darauf Code aufgerufen wird).
Ich würde Option 2 vorschlagen. Option 1 hat eine Race-Bedingung, wenn ein anderer Thread das Attribut hinzufügt oder entfernt.
Auch Python hat eine Redewendung , dass EAFP ("leichter um Vergebung als um Erlaubnis zu bitten") besser ist als LBYL ("schau bevor du springst").
Dieses Thema wurde im EuroPython 2016-Vortrag „ Schnelleres Python schreiben“ von Sebastian Witowski behandelt. Hier ist eine Reproduktion seiner Folie mit der Leistungsübersicht. Er verwendet auch den Terminologie- Look, bevor Sie in diese Diskussion einsteigen. Erwähnenswert ist hier, um dieses Schlüsselwort zu kennzeichnen.
Wenn das Attribut tatsächlich fehlt, ist das Bitten um Vergebung langsamer als das Bitten um Berechtigungen. Als Faustregel können Sie die Frage nach der Erlaubnis verwenden, wenn Sie wissen, dass das Attribut sehr wahrscheinlich fehlt oder andere Probleme, die Sie vorhersagen können. Andernfalls führt Code, wenn Sie erwarten, meistens zu lesbarem Code
# CASE 1 -- Attribute Exists
class Foo(object):
hello = 'world'
foo = Foo()
if hasatter(foo, 'hello'):
foo.hello
## 149ns ##
try:
foo.hello
except AttributeError:
pass
## 43.1 ns ##
## 3.5 times faster
# CASE 2 -- Attribute Absent
class Bar(object):
pass
bar = Bar()
if hasattr(bar, 'hello'):
bar.hello
## 428 ns ##
try:
bar.hello
except AttributeError :
pass
## 536 ns ##
## 25% slower
Aus praktischer Sicht ist die Verwendung einer Bedingung in den meisten Sprachen immer wesentlich schneller als die Behandlung einer Ausnahme.
Wenn Sie den Fall eines Attributs behandeln möchten, das nicht außerhalb der aktuellen Funktion vorhanden ist, ist die Ausnahme der bessere Weg. Ein Indikator dafür, dass Sie möglicherweise eine Ausnahme anstelle einer Bedingung verwenden möchten, ist, dass die Bedingung lediglich ein Flag setzt und die aktuelle Operation abbricht. An anderer Stelle wird dieses Flag überprüft und basierend darauf Maßnahmen ergriffen.
Wie Rax Olgud betont, ist die Kommunikation mit anderen ein wichtiges Attribut des Codes, und was Sie sagen möchten, indem Sie sagen, dass dies eine Ausnahmesituation ist und nicht, dass dies etwas ist, von dem ich erwarte, dass es passiert, ist möglicherweise wichtiger .
Der Erste.
Kürzer ist besser. Ausnahmen sollten außergewöhnlich sein.
for
Anweisung und hasattr
verwendet auch eine. Es gilt jedoch "kürzer ist besser" (und "einfacher ist besser"!). Daher ist das einfachere, kürzere und spezifischere Hasattr in der Tat vorzuziehen.
Zumindest, wenn es nur darum geht, was im Programm vor sich geht, den menschlichen Teil der Lesbarkeit usw. wegzulassen (was eigentlich meistens wichtiger ist als die Leistung (zumindest in diesem Fall - mit dieser Leistungsspanne)). wie Roee Adler und andere betonten).
Wenn man es jedoch aus dieser Perspektive betrachtet, ist es dann eine Frage der Wahl zwischen
try: getattr(obj, attr)
except: ...
und
try: obj.attr
except: ...
da verwendet hasattr
nur den ersten Fall, um das Ergebnis zu bestimmen. Denkanstöße ;-)