Ich finde es oft so, dass ich einen Booleschen Wert von einer Methode zurückgebe, die an mehreren Stellen verwendet wird, um die gesamte Logik um diese Methode an einer einzigen Stelle zu speichern. Die (interne) Aufrufmethode muss nur wissen, ob die Operation erfolgreich war oder nicht.
Ich benutze Python, aber die Frage ist nicht unbedingt spezifisch für diese Sprache. Ich kann mir nur zwei Möglichkeiten vorstellen
- Lösen Sie eine Ausnahme aus, obwohl die Umstände nicht außergewöhnlich sind, und denken Sie daran, diese Ausnahme an jeder Stelle abzufangen, an der die Funktion aufgerufen wird
- Gib einen Booleschen zurück, wie ich es tue.
Dies ist ein wirklich einfaches Beispiel, das zeigt, wovon ich spreche.
import os
class DoSomething(object):
def remove_file(self, filename):
try:
os.remove(filename)
except OSError:
return False
return True
def process_file(self, filename):
do_something()
if remove_file(filename):
do_something_else()
Obwohl es funktioniert, mag ich diese Art, etwas zu tun, nicht, es "riecht" und kann manchmal zu einer Menge verschachtelter Ifs führen. Aber ich kann mir keinen einfacheren Weg vorstellen.
Ich könnte mich os.path.exists(filename)
vor dem Versuch, eine Löschung vorzunehmen, einer LBYL-Philosophie zuwenden, aber es gibt keine Garantie dafür, dass die Datei in der Zwischenzeit nicht gesperrt wurde (dies ist unwahrscheinlich, aber möglich), und ich muss immer noch feststellen, ob die Löschung erfolgreich war oder nicht.
Ist dies ein "akzeptables" Design und wenn nicht, was wäre ein besserer Weg, dies zu gestalten?
Ihre Intuition dazu ist richtig, es gibt einen besseren Weg, dies zu tun: Monaden .
Was sind Monaden?
Monaden sind (um Wikipedia zu umschreiben) eine Möglichkeit, Operationen miteinander zu verketten, während der Verkettungsmechanismus verborgen bleibt. In Ihrem Fall ist der Verkettungsmechanismus das verschachtelte
if
s. Verstecken Sie das und Ihr Code wird viel besser riechen .Es gibt ein paar Monaden, die genau das tun ("Maybe" und "Either") und zum Glück sind sie Teil einer wirklich schönen Python-Monaden-Bibliothek!
Was sie für Ihren Code tun können
Hier ist ein Beispiel mit der Monade "Either" ("Failable" in der verknüpften Bibliothek), in der eine Funktion je nach Ereignis einen Erfolg oder einen Fehler zurückgeben kann:
Das sieht vielleicht nicht viel anders aus als das, was Sie jetzt haben, aber überlegen Sie, wie es wäre, wenn Sie mehr Operationen hätten, die zu einem Fehler führen könnten:
An jeden der
yield
s in derprocess_file
Funktion, wenn der Funktionsaufruf einen Ausfall kehrt dann dieprocess_file
würde Funktion verlassen aus, an diesem Punkt , den Fehlerwert aus der gescheiterten Funktion zurückkehrte, anstatt weiterhin auf durch den Rest und die Rückkehr derSuccess("All ok.")
Stellen Sie sich vor, Sie machen das oben beschriebene mit verschachtelten
if
s! (Wie würden Sie mit dem Rückgabewert umgehen !?)Fazit
Monaden sind nett :)
Anmerkungen:
Ich bin kein Python-Programmierer - ich habe die oben verlinkte Monadenbibliothek in einem Skript verwendet, das ich für eine Projektautomatisierung ninja gemacht habe. Ich stelle jedoch fest, dass der bevorzugte, idiomatische Ansatz im Allgemeinen darin besteht, Ausnahmen zu verwenden.
IIRC Es gibt einen Tippfehler im lib-Skript auf der Seite, auf die verlinkt ist, obwohl ich vergesse, wo es sich um ATM handelt. Ich werde aktualisieren, wenn ich mich erinnere.Ich habe meine Version gegen die der Seite abgeglichen und festgestellt:def failable_monad_examle():
->def failable_monad_example():
- dasp
in hatexample
gefehlt.Um das Ergebnis einer Failable Decorated-Funktion (wie z. B.
process_file
) zu erhalten, müssen Sie das Ergebnis in a erfassenvariable
und a ausführenvariable.value
, um es zu erhalten.quelle
Eine Funktion ist ein Vertrag, und ihr Name sollte angeben, welchen Vertrag sie erfüllen wird. IMHO, wenn Sie es
remove_file
so nennen, sollte es die Datei entfernen , und wenn Sie dies nicht tun, sollte dies eine Ausnahme verursachen. Wenn Sie es jedoch benennentry_remove_file
, sollte es versuchen, es zu entfernen, und einen booleschen Wert zurückgeben, um festzustellen, ob die Datei entfernt wurde oder nicht.Dies würde zu einer anderen Frage führen - sollte es sein
remove_file
odertry_remove_file
? Das hängt von Ihrer Anrufstelle ab. Eigentlich können Sie beide Methoden haben und sie in verschiedenen Szenarien verwenden, aber ich denke, dass das Entfernen von Dateien per se eine hohe Erfolgschance hat, also bevorzuge ich, nurremove_file
diese Ausnahmebedingung zu haben, wenn dies fehlschlägt.quelle
In diesem speziellen Fall kann es hilfreich sein, darüber nachzudenken, warum Sie die Datei möglicherweise nicht entfernen können. Angenommen, das Problem besteht darin, dass die Datei möglicherweise vorhanden ist oder nicht. Dann sollten Sie eine Funktion haben
doesFileExist()
, die true oder false zurückgibt, und eine FunktionremoveFile()
, die nur die Datei löscht.In Ihrem Code würden Sie zuerst überprüfen, ob die Datei vorhanden ist. Wenn ja, rufen Sie an
removeFile
. Wenn nicht, dann mache andere Sachen.In diesem Fall möchten Sie möglicherweise immer noch
removeFile
eine Ausnahme auslösen, wenn die Datei aus einem anderen Grund, z. B. aufgrund von Berechtigungen, nicht entfernt werden kann.Zusammenfassend gesagt, sollten Ausnahmen für Dinge geworfen werden, die außergewöhnlich sind. Wenn es also ganz normal ist, dass die zu löschende Datei nicht existiert, ist dies keine Ausnahme. Schreiben Sie ein boolesches Prädikat, um dies zu überprüfen. Wenn Sie jedoch nicht über die Schreibberechtigungen für die Datei verfügen oder wenn Sie sich in einem fernen Dateisystem befinden, auf das plötzlich nicht mehr zugegriffen werden kann, sind dies möglicherweise Ausnahmebedingungen.
quelle