30 Stimmen

Billige Ausnahmebehandlung in Python?

In einem früheren Artikel las ich Antwort dass die Behandlung von Ausnahmen in Python billig ist, so dass wir nicht vorbedingte Prüfung tun sollten.

Ich habe davon noch nie gehört, aber ich bin relativ neu in Python. Ausnahmebehandlung bedeutet einen dynamischen Aufruf und einen statischen Rücksprung, während ein if Anweisung ist statischer Aufruf, statische Rückkehr.

Wie kann die Kontrolle schlecht sein und die try-except gut zu sein, scheint es andersherum zu sein. Kann mir das jemand erklären?

2 Stimmen

Verwandte Diskussion: stackoverflow.com/questions/204308/

8 Stimmen

+1 Für das Hinterfragen des Evangeliums, anstatt alles zu glauben, was man liest :)

37voto

Ryan Bright Punkte 3417

Kümmern Sie sich nicht um die kleinen Dinge. Sie haben sich bereits für eine der langsameren Skriptsprachen entschieden, so dass der Versuch, bis hinunter zum Opcode zu optimieren, Ihnen nicht viel nützen wird. Der Grund für die Wahl einer interpretierten, dynamischen Sprache wie Python ist es, Ihre Zeit zu optimieren, nicht die der CPU.

Wenn Sie gängige Sprachidiome verwenden, werden Sie alle Vorteile des schnellen Prototyping und des sauberen Designs nutzen können, und Ihr Code wird natürlich schneller laufen, wenn neue Python-Versionen veröffentlicht werden und die Computerhardware aufgerüstet wird.

Wenn Sie Leistungsprobleme haben, sollten Sie Ihren Code profilieren und Ihre langsamen Algorithmen optimieren. Aber in der Zwischenzeit sollten Sie Ausnahmen für Ausnahmesituationen verwenden, da dies jede Umstrukturierung, die Sie letztendlich in dieser Richtung vornehmen, sehr viel einfacher machen wird.

26voto

Jay Punkte 40418

Vielleicht finden Sie diesen Beitrag hilfreich: Try / Außer Leistung in Python: Ein einfacher Test wo Patrick Altman einige einfache Tests durchgeführt hat, um zu sehen, wie die Leistung in verschiedenen Szenarien vor der bedingten Prüfung (speziell für Wörterbuchschlüssel in diesem Fall) und unter Verwendung von Ausnahmen ist. Der Code wird ebenfalls zur Verfügung gestellt, wenn Sie ihn anpassen möchten, um andere Bedingungen zu testen.

Die Schlussfolgerungen, zu denen er kam:

Aufgrund dieser Ergebnisse halte ich es für angemessen schnell eine Reihe von Schlussfolgerungen zu ziehen Schlussfolgerungen:

  1. Wenn eine hohe Wahrscheinlichkeit besteht, dass das Element nicht existiert, dann ist es besser, wenn Sie es mit has_key.
  2. Wenn Sie nichts mit der Ausnahmeregelung anfangen wollen, wenn sie ausgelöst wird, dann ist es besser, wenn Sie die eine Ausnahme zu setzen
  3. Wenn es wahrscheinlich ist, dass das Element tatsächlich existiert, dann gibt es eine sehr Vorteil, einen try/except-Block zu verwenden Block anstelle von has_key zu verwenden, allerdings ist der Vorteil sehr gering.

23voto

Andrew Dalke Punkte 14207

Sieht man einmal von den Leistungsmessungen ab, die andere erwähnt haben, so lautet der Leitsatz oft: "Es ist einfacher, um Vergebung zu bitten, als um Erlaubnis zu fragen" oder "Schau, bevor du springst".

Betrachten Sie diese beiden Ausschnitte:

# Look before you leap
if not os.path.exists(filename):
    raise SomeError("Cannot open configuration file")
f = open(filename)

vs.

# Ask forgiveness ...
try:
  f = open(filename)
except IOError:
  raise SomeError("Cannot open configuration file")

Gleichwertig? Nicht wirklich. Betriebssysteme sind mehrstufige Systeme. Was passiert, wenn die Datei zwischen dem Test auf "vorhanden" und dem Aufruf von "öffnen" gelöscht wurde?

Was passiert, wenn die Datei zwar existiert, aber nicht lesbar ist? Was ist, wenn es sich um einen Verzeichnisnamen und nicht um eine Datei handelt? Es kann viele mögliche Fehlermöglichkeiten geben, und sie alle zu überprüfen ist eine Menge Arbeit. Zumal der Aufruf von "open" bereits all diese möglichen Fehler überprüft und meldet.

Die Leitlinie sollte sein, die Wahrscheinlichkeit eines inkonsistenten Zustands zu verringern, und der beste Weg dafür ist, Ausnahmen anstelle von test/call zu verwenden.

9voto

S.Lott Punkte 371691

"Kann mir das jemand erklären?"

Kommt darauf an.

Hier ist eine Erklärung, aber sie ist nicht hilfreich. Ihre Frage rührt von Ihren Annahmen her. Da die reale Welt im Widerspruch zu Ihren Annahmen steht, muss das bedeuten, dass Ihre Annahmen falsch sind. Das ist keine gute Erklärung, aber deshalb fragst du ja.

"Ausnahmebehandlung bedeutet einen dynamischen Aufruf und eine statische Rückkehr, während eine if-Anweisung ein statischer Aufruf und eine statische Rückkehr ist."

Was bedeutet "dynamischer Aufruf"? Das Durchsuchen von Stack-Frames nach einem Handler? Ich nehme an, das ist es, was Sie meinen. Und ein "statischer Aufruf" ist das Auffinden des Blocks nach der if-Anweisung.

Vielleicht ist dieser "dynamische Aufruf" nicht der kostspieligste Teil des Vorgangs. Vielleicht ist die Auswertung des if-Ausdrucks etwas teurer als das einfachere "try-it-and-fail".

Es hat sich herausgestellt, dass die internen Integritätsprüfungen von Python fast dasselbe sind wie Ihre if-Anweisung, und dass sie sowieso durchgeführt werden müssen. Da Python immer prüft, ist Ihre if-Anweisung (meistens) überflüssig.

Sie können über die Behandlung von Ausnahmen auf niedriger Ebene lesen in http://docs.python.org/c-api/intro.html#exceptions .


bearbeiten

Mehr zur Sache: Die Debatte "wenn" oder "außer" spielt keine Rolle.

Da Ausnahmen billig sind, sollten Sie sie nicht als Leistungsproblem betrachten.

Verwenden Sie das, was Ihren Code ausmacht klar y sinnvoll . Verschwenden Sie keine Zeit mit Mikrooptimierungen wie dieser.

8voto

gimel Punkte 78080

Mit Python ist es einfach, verschiedene Möglichkeiten für die Geschwindigkeit zu prüfen - lernen Sie die Timeit-Modul :

... Beispielsitzung (unter Verwendung der Befehlszeile), die die Kosten der Verwendung von hasattr() gegenüber try/except zum Testen auf fehlende und vorhandene Objektattribute vergleicht.

% timeit.py 'try:' '  str.__nonzero__' 'except AttributeError:' '  pass'
100000 loops, best of 3: 15.7 usec per loop
% timeit.py 'if hasattr(str, "__nonzero__"): pass'
100000 loops, best of 3: 4.26 usec per loop
% timeit.py 'try:' '  int.__nonzero__' 'except AttributeError:' '  pass'
1000000 loops, best of 3: 1.43 usec per loop
% timeit.py 'if hasattr(int, "__nonzero__"): pass'
100000 loops, best of 3: 2.23 usec per loop

Diese zeitlichen Ergebnisse zeigen in der hasattr() Fall ist das Auslösen einer Ausnahme langsam, aber die Durchführung eines Tests ist langsamer als das Nichtauslösen der Ausnahme. In Bezug auf die Laufzeit ist die Verwendung einer Ausnahme für die Behandlung von außergewöhnlich Fälle Sinn macht.

EDIT: Die Kommandozeilenoption -n wird standardmäßig auf eine ausreichend große Anzahl eingestellt, damit die Laufzeit sinnvoll ist. A Zitat aus dem Handbuch :

Wenn -n nicht angegeben wird, wird eine geeignete Anzahl von Schleifen berechnet, indem aufeinanderfolgende Potenzen von 10 versucht werden, bis die Gesamtzeit mindestens 0,2 Sekunden beträgt.

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