Python: è una cattiva forma sollevare eccezioni in __init__?


128

È considerata una cattiva forma sollevare delle eccezioni all'interno __init__? In tal caso, qual è il metodo accettato per generare un errore quando alcune variabili di classe vengono inizializzate come Noneo di tipo errato?


In C, è una cattiva forma sollevare un'eccezione nel distruttore, ma il costruttore dovrebbe andare bene.
Calyth,

6
@Calyth, intendi C ++ - non ci sono costruttori o distruttori in C.
Alex Martelli,

4
... o eccezioni, del resto.
Matt Wilding,

@Calyth: lol, per favore rimuovi quell'assurda affermazione fatta da un errore di battitura innocente ;-)
lpapp

4
@lpapp si. C ++. FML. E non ho potuto modificare quel commento. Quindi Internet documenterà la mia idiozia;)
Calyth,

Risposte:


159

Sollevare eccezioni all'interno __init__()va assolutamente bene. Non esiste un altro buon modo per indicare una condizione di errore all'interno di un costruttore e ci sono molte centinaia di esempi nella libreria standard in cui la costruzione di un oggetto può sollevare un'eccezione.

La classe di errore da raccogliere, ovviamente, dipende da te. ValueErrorè meglio se al costruttore è stato passato un parametro non valido.


14
Le eccezioni più utilizzate nel costruttore sono ValueErrore TypeError.
Denis Otkidach,

9
Sto solo sottolineando che __init__non è il costruttore, è l'inizializzatore. Potresti voler modificare la riga Non c'è altro buon modo per indicare una condizione di errore all'interno di un costruttore, ..
Haris,

26

È vero che l'unico modo corretto per indicare un errore in un costruttore è sollevare un'eccezione. Questo è il motivo per cui in C ++ e in altri linguaggi orientati agli oggetti che sono stati progettati tenendo presente la sicurezza delle eccezioni, il distruttore non viene chiamato se viene generata un'eccezione nel costruttore di un oggetto (il che significa che l'inizializzazione dell'oggetto è incompleta). Questo non è spesso il caso di linguaggi di scripting, come Python. Ad esempio, il codice seguente genera un AttributeError se socket.connect () fallisce:

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()

Il motivo è che il distruttore dell'oggetto incompleto viene chiamato dopo che il tentativo di connessione ha avuto esito negativo, prima che l'inizializzazione dell'attributo stream. Non dovresti evitare di generare eccezioni dai costruttori, sto solo dicendo che è difficile scrivere codice completamente sicuro in Python. Alcuni sviluppatori di Python evitano del tutto l'uso dei distruttori, ma si tratta di un altro dibattito.


Questa risposta è davvero utile. Non sta solo parlando della "luce verde", ma menziona un esempio con il "distruttore".
lpapp,

Il tuo codice Python fallisce __del__perché è scritto male. Avresti esattamente lo stesso problema se lo facessi this->p_socket->close()in un distruttore in C ++. In C ++, non lo faresti: lasceresti che l'oggetto membro si distrugga da solo. Fai lo stesso in Python.
Eric

1
@Eric Non avrei il problema in C ++ perché C ++ non distrugge gli oggetti che non sono stati inizializzati correttamente . In realtà è un linguaggio molto comune in C ++ allocare risorse nel costruttore e deallocarle nel distruttore . Suggerisci che l'oggetto membro si distrugga da solo (deallocando le risorse nel suo distruttore) - quindi lo stesso problema si presenta quando si scrive la classe membro.
Seppo Enarvi,

11

Non vedo alcun motivo per cui dovrebbe essere una cattiva forma.

Al contrario, una delle cose che le eccezioni sono note per fare bene, al contrario della restituzione di codici di errore, è che i codici di errore di solito non possono essere restituiti dai costruttori. Quindi, almeno in linguaggi come il C ++, sollevare eccezioni è l'unico modo per segnalare errori.


6

La libreria standard dice:

>>> 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'

Inoltre non vedo davvero alcun motivo per cui dovrebbe essere considerata una cattiva forma.


3

Dovrei pensare che sia il caso perfetto per l' ValueErroreccezione integrata .


2

Sono d'accordo con tutto quanto sopra.

Non c'è davvero altro modo per segnalare che qualcosa è andato storto nell'inizializzazione di un oggetto se non quello di sollevare un'eccezione.

Nella maggior parte delle classi di programmi in cui lo stato di una classe dipende interamente dagli input di quella classe, potremmo aspettarci che venga generato un tipo di ValueError o TypeError.

Le classi con effetti collaterali (ad esempio uno che fa rete o grafica) potrebbero generare un errore in init se (ad esempio) il dispositivo di rete non è disponibile o non è possibile scrivere l'oggetto canvas. Mi sembra sensato perché spesso vuoi conoscere al più presto le condizioni di guasto.


2

Generare errori da init è inevitabile in alcuni casi, ma fare troppo lavoro in init è uno stile sbagliato. Dovresti considerare di creare una fabbrica o una pseudo-fabbrica - un semplice metodo di classe che restituisce un oggetto impostato.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.