Verwendung von „try“ vs. „if“ in Python

Lesezeit: 7 Minuten

artdanils Benutzeravatar
artdanil

Gibt es eine Begründung für die Entscheidung, welches davon? try oder if Konstrukte, die beim Testen einer Variablen auf einen Wert verwendet werden sollen?

Beispielsweise gibt es eine Funktion, die entweder eine Liste oder keinen Wert zurückgibt. Ich möchte das Ergebnis überprüfen, bevor ich es verarbeite. Welche der folgenden wäre vorzuziehen und warum?

result = function();
if (result):
    for r in result:
        #process items

oder

result = function();
try:
    for r in result:
        # Process items
except TypeError:
    pass;

Verwandte Diskussion:

Überprüfung auf Mitgliederexistenz in Python

  • Denken Sie daran, dass Sie, wenn Ihre #process items-Strophe dazu neigt, einen TypeError auszulösen, einen weiteren try: außer:-Block einschließen müssen. Für dieses spezielle Beispiel würde ich einfach ein if verwenden:

    – richo

    3. Dezember 2009 um 0:45

Benutzer-Avatar von Tim Pietzcker
Tim Pietzcker

Man hört oft, dass Python fördert EAFP Stil („Es ist einfacher, um Vergebung zu bitten als um Erlaubnis“) vorbei LBYL Stil („Schauen Sie nach, bevor Sie springen“). Für mich ist es eine Frage der Effizienz und Lesbarkeit.

In Ihrem Beispiel (sagen wir, dass die Funktion anstelle einer Liste oder einer leeren Zeichenfolge eine Liste oder zurückgeben sollte None), wenn man das in 99 % der Fälle erwartet result wird tatsächlich etwas Iterierbares enthalten, ich würde das verwenden try/except Ansatz. Es wird schneller gehen, wenn Ausnahmen wirklich außergewöhnlich sind. Wenn result Ist None mehr als 50 % der Zeit, dann verwenden if ist wahrscheinlich besser.

Um dies mit ein paar Messungen zu untermauern:

>>> import timeit
>>> timeit.timeit(setup="a=1;b=1", stmt="a/b") # no error checking
0.06379691968322732
>>> timeit.timeit(setup="a=1;b=1", stmt="try:\n a/b\nexcept ZeroDivisionError:\n pass")
0.0829463709378615
>>> timeit.timeit(setup="a=1;b=0", stmt="try:\n a/b\nexcept ZeroDivisionError:\n pass")
0.5070195056614466
>>> timeit.timeit(setup="a=1;b=1", stmt="if b!=0:\n a/b")
0.11940114974277094
>>> timeit.timeit(setup="a=1;b=0", stmt="if b!=0:\n a/b")
0.051202772912802175

Also, während ein if Stellungnahme stets kostet Sie, die Einrichtung ist nahezu kostenlos try/except Block. Aber wenn ein Exception tatsächlich auftritt, sind die Kosten viel höher.

Moral:

  • Die Verwendung ist vollkommen in Ordnung (und „pythonisch“) try/except zur Durchflusskontrolle,
  • aber es macht am meisten Sinn, wenn Exceptions sind tatsächlich außergewöhnlich.

Aus den Python-Dokumenten:

EAFP

Es ist einfacher, um Vergebung zu bitten als um Erlaubnis. Dieser gängige Python-Codierungsstil geht von der Existenz gültiger Schlüssel oder Attribute aus und fängt Ausnahmen ab, wenn sich die Annahme als falsch erweist. Dieser klare und schnelle Stil zeichnet sich durch die Präsenz vieler aus
try Und except Aussagen. Die Technik steht im Gegensatz zu der LBYL
Stil, der vielen anderen Sprachen wie C gemeinsam ist.

  • Danke schön. Ich sehe, dass in diesem Fall die Erwartung des Ergebnisses der Grund sein kann.

    – artdanil

    2. Dezember 2009 um 21:31 Uhr

  • …. und das ist einer (von vielen) Gründen, warum es so schwierig ist, einen wirklich optimierenden JIT für Python zu erstellen. Wie das kürzlich in der Beta befindliche LuaJIT 2 beweist, dynamische Sprachen dürfen sei wirklich sehr, sehr schnell; aber es hängt stark vom ursprünglichen Sprachdesign und dem Stil ab, den es fördert. (In diesem Zusammenhang ist das Sprachdesign der Grund, warum selbst die allerbesten JavaScirpt-JITs nicht mit LuaJIT 1, geschweige denn mit 2, mithalten können.)

    – Javier

    2. Dezember 2009 um 21:33

  • @2rs2ts: Ich habe gerade selbst ähnliche Zeiten ermittelt. In Python 3, try/except war 25 % schneller als if key in d: für Fälle, in denen der Schlüssel im Wörterbuch enthalten war. Es war wie erwartet viel langsamer, wenn der Schlüssel nicht im Wörterbuch enthalten war, und stimmte mit dieser Antwort überein.

    – Tim Pietzcker

    6. Juli 2013 um 8:49

  • Ich weiß jedoch, dass dies eine alte Antwort ist: Verwenden von Aussagen wie 1/1 Mit der Zeit ist es keine gute Wahl, da sie optimiert werden (siehe dis.dis('1/1') und beachten Sie, dass keine Teilung erfolgt).

    – Andrea Corbellini

    23. August 2015 um 14:59

  • Es hängt auch von der Operation ab, die Sie durchführen möchten. Eine einzelne Division ist schnell, das Erkennen eines Fehlers ist etwas teuer. Aber auch in Fällen, in denen die Kosten für die Ausnahmebehandlung akzeptabel sind, sollten Sie sorgfältig darüber nachdenken, was Sie vom Computer verlangen. Wenn die Operation selbst unabhängig von den Ergebnissen sehr teuer ist und es eine kostengünstige Möglichkeit gibt, festzustellen, ob ein Erfolg möglich ist: Nutzen Sie sie. Beispiel: Kopieren Sie nicht die Hälfte einer Datei, nur um festzustellen, dass nicht genügend Speicherplatz vorhanden ist.

    – Bachsau

    15. Juni 2019 um 14:25

Ihre Funktion sollte keine gemischten Typen (z. B. Liste oder leere Zeichenfolge) zurückgeben. Es sollte eine Liste von Werten oder nur eine leere Liste zurückgeben. Dann müssten Sie nichts testen, d. h. Ihr Code reduziert sich auf:

for r in function():
    # process items

  • In diesem Punkt stimme ich Ihnen voll und ganz zu; Allerdings gehört die Funktion nicht mir und ich nutze sie einfach.

    – artdanil

    2. Dezember 2009 um 21:13

  • @artdanil: Sie könnten diese Funktion also in eine von Ihnen geschriebene Funktion einbinden, die so funktioniert wie die, an die Brandon Corfman denkt.

    – Quamrana

    2. Dezember 2009 um 22:33

  • Was die Frage aufwirft: Sollte der Wrapper ein if oder einen try verwenden, um den nicht iterierbaren Fall zu behandeln?

    – jcdyer

    3. Dezember 2009 um 18:24

  • @quamrana, das ist genau das, was ich versuche. Wie @jcd betonte: Die Frage ist, wie ich mit der Situation in der Wrapper-Funktion umgehen soll.

    – artdanil

    3. Dezember 2009 um 20:51

Bitte ignorieren Sie meine Lösung, wenn der von mir bereitgestellte Code nicht auf den ersten Blick offensichtlich ist und Sie die Erklärung nach dem Codebeispiel lesen müssen.

Kann ich davon ausgehen, dass „kein zurückgegebener Wert“ bedeutet, dass der Rückgabewert „Keiner“ ist? Wenn ja, oder wenn „kein Wert“ boolesch-mäßig falsch ist, können Sie Folgendes tun, da Ihr Code „kein Wert“ im Wesentlichen als „nicht iterieren“ behandelt:

for r in function() or ():
    # process items

Wenn function() etwas zurückgibt, das nicht True ist, iterieren Sie über das leere Tupel, dh Sie führen keine Iterationen durch. Dies ist im Wesentlichen LBYL.

Managus Benutzeravatar
Managu

Generell habe ich den Eindruck gewonnen, dass Ausnahmen außergewöhnlichen Umständen vorbehalten sein sollten. Wenn die result Da davon auszugehen ist, dass der Speicher nie leer ist (was aber beispielsweise der Fall sein kann, wenn eine Festplatte abstürzt usw.), ist der zweite Ansatz sinnvoll. Wenn hingegen ein leerer result Unter normalen Bedingungen ist es durchaus sinnvoll, es mit einem zu testen if Aussage macht mehr Sinn.

Ich hatte das (häufigere) Szenario im Sinn:

# keep access counts for different files
file_counts={}
...
# got a filename somehow
if filename not in file_counts:
    file_counts[filename]=0
file_counts[filename]+=1

statt des Äquivalents:

...
try:
    file_counts[filename]+=1
except KeyError:
    file_counts[filename]=1

Welche der folgenden wäre vorzuziehen und warum?

In diesem Fall ist „Look Before You Leap“ vorzuziehen. Mit dem Ausnahmeansatz könnte irgendwo in Ihrem Schleifenkörper ein TypeError auftreten, der abgefangen und weggeworfen wird. Das ist nicht das, was Sie wollen, und macht das Debuggen schwierig.

(Ich stimme jedoch Brandon Corfman zu: Die Rückgabe von None für „keine Elemente“ anstelle einer leeren Liste ist fehlerhaft. Es ist eine unangenehme Angewohnheit von Java-Programmierern, die in Python oder Java nicht vorkommen sollte.)

Dave Kirbys Benutzeravatar
Dave Kirby

Ihr zweites Beispiel ist fehlerhaft – der Code löst niemals eine TypeError-Ausnahme aus, da Sie sowohl Zeichenfolgen als auch Listen durchlaufen können. Das Durchlaufen einer leeren Zeichenfolge oder Liste ist ebenfalls zulässig – der Schleifenkörper wird dabei null Mal ausgeführt.

Benutzeravatar der Community
Gemeinschaft

Bobince weist klugerweise darauf hin, dass das Umschließen des zweiten Falls auch TypeErrors in der Schleife abfangen kann, was nicht das ist, was Sie wollen. Wenn Sie jedoch wirklich einen Versuch verwenden möchten, können Sie vor der Schleife testen, ob er iterierbar ist

result = function();
try:
    it = iter(result)
except TypeError:
    pass
else:
    for r in it:
        #process items

Wie Sie sehen, ist es ziemlich hässlich. Ich empfehle es nicht, aber der Vollständigkeit halber sollte es erwähnt werden.

  • In meinen Augen ist es immer ein schlechter Programmierstil, absichtlich auf Tippfehler zu stoßen. Ein Entwickler sollte wissen, was ihn erwartet, und bereit sein, mit diesen Werten ausnahmslos umzugehen. Immer wenn eine Operation das getan hat, was sie tun sollte (aus Sicht des Programmierers) und die erwarteten Ergebnisse eine Liste oder sind Noneüberprüfen Sie das Ergebnis immer mit is None oder is not None. Andererseits ist es auch ein schlechter Stil, Ausnahmen für legitime Ergebnisse auszulösen. Ausnahmen gelten für unerwartete Dinge. Beispiel: str.find() Gibt -1 zurück, wenn nichts gefunden wird, da die Suche selbst ohne Fehler abgeschlossen wurde.

    – Bachsau

    15. Juni 2019 um 14:41

1450150cookie-checkVerwendung von „try“ vs. „if“ in Python

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy