Python - behaupten vs if & return

12

Ich schreibe ein Skript, das etwas mit einer Textdatei macht (was es tut, ist für meine Frage jedoch irrelevant). Bevor ich also etwas mit der Datei anfange, möchte ich überprüfen, ob die Datei existiert. Ich kann das, kein Problem, aber das Problem ist mehr das der Ästhetik.

Hier ist mein Code, der dasselbe auf zwei verschiedene Arten implementiert.

def modify_file(filename):
    assert os.path.isfile(filename), 'file does NOT exist.'


Traceback (most recent call last):
  File "clean_files.py", line 15, in <module>
    print(clean_file('tes3t.txt'))
  File "clean_files.py", line 8, in clean_file
    assert os.path.isfile(filename), 'file does NOT exist.'
AssertionError: file does NOT exist.

oder:

def modify_file(filename):
    if not os.path.isfile(filename):
        return 'file does NOT exist.'


file does NOT exist.

Die erste Methode erzeugt eine Ausgabe, die meistens trivial ist. Das einzige, was mich interessiert, ist, dass die Datei nicht existiert.

Die zweite Methode gibt einen String zurück, es ist einfach.

Meine Fragen sind: Welche Methode ist besser, um den Benutzer wissen zu lassen, dass die Datei nicht vorhanden ist? Die assertMethode scheint irgendwie pythonischer zu sein.

Vader
quelle

Antworten:

33

Sie würden stattdessen eine dritte Option wählen: use raiseund eine bestimmte Ausnahme. Dies kann eine der integrierten Ausnahmen sein , oder Sie können eine benutzerdefinierte Ausnahme für den Job erstellen.

In diesem Fall würde ich verwenden IOError, aber ein ValueErrorkönnte auch passen:

def modify_file(filename):
    if not os.path.isfile(filename):
        raise IOError('file does NOT exist.')

Durch die Verwendung einer bestimmten Ausnahme können Sie andere Ausnahmen für andere Ausnahmesituationen auslösen und der Aufrufer kann die Ausnahme ordnungsgemäß behandeln.

Natürlich sind viele Dateioperationen (wie open()) sich erheben OSErrorbereits; Der explizite Ersttest, ob die Datei existiert, kann hier redundant sein.

Nicht verwenden assert; Wenn Sie Python mit dem -OFlag ausführen , werden alle Zusicherungen aus dem Code entfernt.

Martijn Pieters
quelle
Interessanter Punkt zur Redundanz! Würden Sie empfehlen, dies zu vermeiden? dh "es wird sowieso später scheitern"
Ciprian Tomoiagă
1
@ CiprianTomoiagă: Warum bei Tests verdoppeln? Wenn die nächste Zeile von modify_file()ist with open(filename) as f:, dann IOErrorwürde auch angehoben werden. Und neuere Python-Versionen enthalten detailliertere Informationen in Unterklassen von IOError( FileNotFoundErrorspeziell in den Sinn kommenden) Elementen, die für Entwickler, die diese API verwenden, hilfreich sein können. Wenn der Code selbst prüft und auslöst, geht IOErrordieses hilfreiche Detail verloren.
Martijn Pieters
@MartijnPieters wäre eine akzeptable Antwort auf "Warum doppelte Tests?" Wann ist die erste Prüfung schneller als die Ausnahme, die ausgelöst wird, wenn open () fehlschlägt? ZB wenn die Prüfung auf das Vorhandensein einer Datei schneller ist als der Versuch, sie zu öffnen und dies letztendlich nicht zu tun.
Marcel Wilson
1
@MarcelWilson: Nein, weil Sie gegen eine Methode, die I / O ausführt, eine Mikrooptimierung durchführen würden. Keine Änderung der Python-Semantik in der Minute beschleunigt die E / A und beeinträchtigt nur die Lesbarkeit und Wartbarkeit. Konzentrieren Sie sich auf Bereiche mit mehr Wirkung, würde ich sagen.
Martijn Pieters
12

assertist für Fälle gedacht, in denen der Programmierer , der die Funktion aufruft, im Gegensatz zum Benutzer einen Fehler gemacht hat . Unter assertdiesen Umständen können Sie sicherstellen, dass ein Programmierer Ihre Funktion während des Testens korrekt verwendet, diese jedoch in der Produktion entfernt.

Ihr Wert ist etwas begrenzt, da Sie sicherstellen müssen, dass Sie diesen Pfad durch den Code gehen, und Sie das Problem häufig zusätzlich mit einer separaten ifAnweisung in der Produktion behandeln möchten . assertist am nützlichsten in Situationen wie "Ich möchte dieses Problem hilfreich umgehen, wenn ein Benutzer es trifft, aber wenn ein Entwickler es trifft, möchte ich, dass es hart abstürzt, damit er den Code korrigiert, der diese Funktion falsch aufruft."

In Ihrem speziellen Fall ist eine fehlende Datei mit ziemlicher Sicherheit ein Benutzerfehler und sollte durch Auslösen einer Ausnahme behoben werden.

Karl Bielefeldt
quelle
5

von UsingAssertionsEffectively

Das Überprüfen von isinstance () sollte nicht überbeansprucht werden: Wenn es wie eine Ente quakt, besteht möglicherweise keine Notwendigkeit, zu tief nachzufragen, ob es wirklich so ist. Manchmal kann es nützlich sein, Werte zu übergeben, die vom ursprünglichen Programmierer nicht erwartet wurden.

Stellen, an denen Sie Behauptungen aufstellen sollten:

checking parameter types, classes, or values
checking data structure invariants
checking "can't happen" situations (duplicates in a list, contradictory state variables.)
after calling a function, to make sure that its return is reasonable 

Der springende Punkt ist, dass wir, wenn etwas schief geht, es so schnell wie möglich klarstellen wollen.

Es ist einfacher, falsche Daten an der Stelle zu erfassen, an der sie eingehen, als später herauszufinden, wie sie dort ankommen, wenn es Probleme gibt.

Behauptungen sind kein Ersatz für Unit-Tests oder Systemtests, sondern eine Ergänzung. Da Zusicherungen eine saubere Möglichkeit sind, den internen Zustand eines Objekts oder einer Funktion zu untersuchen, bieten sie "kostenlos" eine eindeutige Unterstützung für einen Black-Box-Test, der das externe Verhalten untersucht.

Assertions sollten nicht zum Testen auf Fehlerfälle verwendet werden, die aufgrund falscher Benutzereingaben oder Betriebssystem- / Umgebungsfehlern auftreten können, z. B. wenn eine Datei nicht gefunden wird. Stattdessen sollten Sie eine Ausnahme auslösen oder eine Fehlermeldung ausgeben, oder was auch immer angemessen ist. Ein wichtiger Grund, warum Zusicherungen nur für Selbsttests des Programms verwendet werden sollten, besteht darin, dass Zusicherungen zum Zeitpunkt der Kompilierung deaktiviert werden können.

Wenn Python mit der Option -O gestartet wird, werden Zusicherungen entfernt und nicht ausgewertet. Wenn Code Assertions stark verwendet, aber leistungskritisch ist, gibt es ein System, mit dem diese in Release-Builds deaktiviert werden können. (Tun Sie dies jedoch nicht, es sei denn, dies ist wirklich notwendig. Es ist wissenschaftlich erwiesen, dass einige Fehler nur dann auftreten, wenn ein Kunde die Maschine verwendet, und wir möchten, dass auch dort Behauptungen hilfreich sind. :-))

dspjm
quelle