Python: Ist es eine schlechte Form, Ausnahmen innerhalb von __init__ auszulösen?

128

Wird es als schlechte Form angesehen, Ausnahmen innerhalb zu erheben __init__? Wenn ja, wie wird dann ein Fehler ausgegeben, wenn bestimmte Klassenvariablen als Noneoder von falschem Typ initialisiert werden ?

krizajb
quelle
In C ist es eine schlechte Form, eine Ausnahme im Destruktor auszulösen, aber der Konstruktor sollte in Ordnung sein.
Calyth
6
@Calyth, du meinst C ++ - es gibt keine Konstruktoren oder Destruktoren in C.
Alex Martelli
4
... oder Ausnahmen.
Matt Wilding
@Calyth: lol, bitte entfernen Sie diese unsinnige Aussage eines unschuldigen Tippfehlers ;-)
lpapp
4
@lpapp ja. C ++. FML. Und ich konnte diesen Kommentar nicht bearbeiten. Also soll das Internet meine Idiotie dokumentieren;)
Calyth

Antworten:

159

Ausnahmen innerhalb zu erheben __init__()ist absolut in Ordnung. Es gibt keine andere gute Möglichkeit, eine Fehlerbedingung innerhalb eines Konstruktors anzuzeigen, und es gibt viele hundert Beispiele in der Standardbibliothek, bei denen das Erstellen eines Objekts eine Ausnahme auslösen kann.

Die Fehlerklasse, die ausgelöst werden soll, liegt natürlich bei Ihnen. ValueErrorist am besten, wenn dem Konstruktor ein ungültiger Parameter übergeben wurde.

John Millikin
quelle
14
Die am häufigsten verwendeten Ausnahmen im Konstruktor sind ValueErrorund TypeError.
Denis Otkidach
9
__init__Ich möchte nur darauf hinweisen, dass dies nicht der Konstruktor, sondern der Intializer ist. Vielleicht möchten Sie die Zeile bearbeiten. Es gibt keine andere gute Möglichkeit, eine Fehlerbedingung innerhalb eines Konstruktors anzuzeigen.
Haris
26

Es ist wahr, dass der einzig richtige Weg, um einen Fehler in einem Konstruktor anzuzeigen, darin besteht, eine Ausnahme auszulösen. Aus diesem Grund wird in C ++ und anderen objektorientierten Sprachen, die unter Berücksichtigung der Ausnahmesicherheit entwickelt wurden, der Destruktor nicht aufgerufen, wenn im Konstruktor eines Objekts eine Ausnahme ausgelöst wird (was bedeutet, dass die Initialisierung des Objekts unvollständig ist). Dies ist in Skriptsprachen wie Python häufig nicht der Fall. Der folgende Code löst beispielsweise einen AttributeError aus, wenn socket.connect () fehlschlägt:

class NetworkInterface:
    def __init__(self, address)
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.socket.connect(address)
        self.stream = self.socket.makefile()

    def __del__(self)
        self.stream.close()
        self.socket.close()

Der Grund ist, dass der Destruktor des unvollständigen Objekts aufgerufen wird, nachdem der Verbindungsversuch fehlgeschlagen ist, bevor das Stream-Attribut initialisiert wurde. Sie sollten es nicht vermeiden, Ausnahmen von Konstruktoren auszulösen. Ich sage nur, dass es schwierig ist, vollständig ausnahmesicheren Code in Python zu schreiben. Einige Python-Entwickler vermeiden die Verwendung von Destruktoren insgesamt, aber das ist eine Frage einer anderen Debatte.

Seppo Enarvi
quelle
Diese Antwort ist wirklich nützlich. Es geht nicht nur um das "grüne Licht", sondern es wird ein Beispiel mit dem "Destruktor" erwähnt.
lpapp
Ihr Python-Code schlägt fehl, __del__weil er schlecht geschrieben ist. Sie hätten genau das gleiche Problem, wenn Sie es this->p_socket->close()in einem Destruktor in C ++ getan hätten . In C ++ würden Sie das nicht tun - Sie würden das Mitgliedsobjekt sich selbst zerstören lassen. Machen Sie dasselbe in Python.
Eric
1
@Eric Ich hätte das Problem in C ++ nicht, weil C ++ keine Objekte zerstört, die nicht richtig initialisiert wurden . Tatsächlich ist es in C ++ eine weit verbreitete Redewendung, Ressourcen im Konstruktor zuzuweisen und im Destruktor freizugeben . Sie schlagen vor, dass sich das Member-Objekt selbst zerstört (Freigabe der Ressourcen in seinem Destruktor) - dann tritt das gleiche Problem beim Schreiben der Member-Klasse auf.
Seppo Enarvi
11

Ich sehe keinen Grund, warum es eine schlechte Form sein sollte.

Im Gegenteil, eine der Ausnahmen ist bekannt dafür, dass Fehlercodes im Gegensatz zur Rückgabe von Fehlercodes normalerweise nicht von Konstruktoren zurückgegeben werden können. Zumindest in Sprachen wie C ++ ist das Auslösen von Ausnahmen die einzige Möglichkeit, Fehler zu signalisieren.

Edan Maor
quelle
6

Die Standardbibliothek sagt:

>>> f = file("notexisting.txt")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IOError: [Errno 2] No such file or directory: 'notexisting.txt'

Ich sehe auch keinen Grund, warum es als schlechte Form angesehen werden sollte.

etw
quelle
3

Ich sollte denken, dass dies der perfekte Fall für die eingebaute ValueErrorAusnahme ist.

Teddy
quelle
2

Ich stimme all dem zu.

Es gibt wirklich keinen anderen Weg, um zu signalisieren, dass bei der Initialisierung eines Objekts ein Fehler aufgetreten ist, als eine Ausnahme auszulösen.

In den meisten Programmklassen, in denen der Status einer Klasse vollständig von den Eingaben in diese Klasse abhängt, können wir erwarten, dass eine Art ValueError oder TypeError ausgelöst wird.

Klassen mit Nebenwirkungen (z. B. solche, die Netzwerke oder Grafiken ausführen) können einen Fehler in init auslösen, wenn (zum Beispiel) das Netzwerkgerät nicht verfügbar ist oder das Canvas-Objekt nicht beschrieben werden kann. Das klingt für mich sinnvoll, weil Sie oft so schnell wie möglich über Fehlerzustände informiert werden möchten.

Salim Fadhley
quelle
2

Das Auslösen von Fehlern aus init ist in einigen Fällen unvermeidbar, aber zu viel Arbeit in init ist ein schlechter Stil. Sie sollten in Betracht ziehen, eine Factory oder eine Pseudo-Factory zu erstellen - eine einfache Klassenmethode, die ein festgelegtes Objekt zurückgibt.

Strg-C
quelle