Python: ¿es una mala forma generar excepciones dentro de __init__?

128

¿Se considera mala forma de hacer excepciones dentro __init__? Si es así, ¿cuál es el método aceptado para arrojar un error cuando ciertas variables de clase se inicializan como Noneo de un tipo incorrecto?

krizajb
fuente
En C, es una mala forma generar una excepción en el destructor, pero el constructor debería estar bien.
Calyth
66
@Calyth, te refieres a C ++ - no hay constructores ni destructores en C.
Alex Martelli
44
... o excepciones, para el caso.
Matt Wilding
@Calyth: lol, por favor elimine esa declaración sin sentido hecha por un error tipográfico inocente ;-)
lpapp
44
@lpapp sí. C ++. FML Y no pude editar ese comentario. Entonces, Internet documentará mi idiotez;)
Calyth

Respuestas:

159

Elevar excepciones dentro __init__()está absolutamente bien. No hay otra buena manera de indicar una condición de error dentro de un constructor, y hay muchos cientos de ejemplos en la biblioteca estándar donde construir un objeto puede generar una excepción.

La clase de error a plantear, por supuesto, depende de usted. ValueErrores mejor si el constructor pasó un parámetro no válido.

John Millikin
fuente
14
Las excepciones más utilizadas en constructor son ValueErrory TypeError.
Denis Otkidach
9
Solo señalando que __init__no es el constructor, es el intializador. Es posible que desee editar la línea No hay otra buena manera de indicar una condición de error dentro de un constructor, ..
Haris
26

Es cierto que la única forma correcta de indicar un error en un constructor es generar una excepción. Es por eso que en C ++ y en otros lenguajes orientados a objetos que se han diseñado teniendo en cuenta la seguridad de la excepción, no se llama al destructor si se produce una excepción en el constructor de un objeto (lo que significa que la inicialización del objeto está incompleta). Este no suele ser el caso en los lenguajes de secuencias de comandos, como Python. Por ejemplo, el siguiente código arroja un AttributeError si socket.connect () falla:

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

La razón es que se llama al destructor del objeto incompleto después de que el intento de conexión ha fallado, antes de que se haya inicializado el atributo de flujo. No debe evitar arrojar excepciones de los constructores, solo digo que es difícil escribir código seguro de excepciones en Python. Algunos desarrolladores de Python evitan usar destructores por completo, pero eso es otro tema de debate.

Seppo Enarvi
fuente
Esta respuesta es realmente útil. No solo se trata de la "luz verde", sino que se menciona un ejemplo con el "destructor".
lpapp
Su código de Python falla __del__porque está mal escrito. Tendría exactamente el mismo problema si lo hiciera this->p_socket->close()en un destructor en C ++. En C ++, no haría eso, dejaría que el objeto miembro se destruya a sí mismo. Haz lo mismo en python.
Eric
1
@Eric No tendría el problema en C ++ porque C ++ no destruye los objetos que no se han inicializado correctamente . En realidad, es un lenguaje muy común en C ++ asignar recursos en el constructor y desasignarlos en el destructor . Sugiere que el objeto miembro se destruya a sí mismo (desasignando los recursos en su destructor), luego surge el mismo problema al escribir la clase miembro.
Seppo Enarvi
11

No veo ninguna razón para que sea una mala forma.

Por el contrario, una de las cosas por las que se sabe que las excepciones funcionan bien, en lugar de devolver códigos de error, es que los constructores no pueden devolver los códigos de error . Entonces, al menos en lenguajes como C ++, generar excepciones es la única forma de señalar errores.

Edan Maor
fuente
6

La biblioteca estándar 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'

Además, realmente no veo ninguna razón por la que deba considerarse una mala forma.

algo
fuente
3

Debería pensar que es el caso perfecto para la ValueErrorexcepción incorporada .

Osito de peluche
fuente
2

Estoy de acuerdo con todo lo anterior.

Realmente no hay otra forma de indicar que algo salió mal en la inicialización de un objeto que no sea generar una excepción.

En la mayoría de las clases de programas donde el estado de una clase depende totalmente de las entradas a esa clase, podríamos esperar que se genere algún tipo de ValueError o TypeError.

Las clases con efectos secundarios (p. Ej., Una red o gráficos) pueden generar un error en init si (por ejemplo) el dispositivo de red no está disponible o no se puede escribir en el objeto del lienzo. Esto me parece razonable porque a menudo quieres saber sobre las condiciones de falla lo antes posible.

Salim Fadhley
fuente
2

Elevar errores desde init es inevitable en algunos casos, pero hacer demasiado trabajo en init es un mal estilo. Debería considerar hacer una fábrica o una pseudo-fábrica, un método de clase simple que devuelve un objeto configurado.

Ctrl-C
fuente